[Mono-devel-list] Patch for full-featured mcs /doc support

Marek Safar marek.safar at seznam.cz
Fri Nov 26 05:25:53 EST 2004


Hello Eno,

>Ok, the harness.mk problem was from both xmldocdiff.cs and
>makefiles. Additionally I have to modify decl.cs a bit (to
>mimick csc output for invalid "include" element) and had
>to make a trick for xml-025.cs that handles inclusion that
>csc cannot handle '/' file path separator expectedly.
>
>Sorry for flooding the patches. Please review this latest one.
>  
>
OK,

+		if (RootContext.NeedDocument)
+			if (Lexer.doc_state == XmlCommentState.Error)
+				Lexer.doc_state = XmlCommentState.NotAllowed;

Please change to && (2 places)

+ private Type [] emptyParamList = new Type [0];


Should be static.

That is all

Marek

>Atsushi Eno
>
>Atsushi Eno wrote:
>  
>
>>Hello,
>>
>>Thanks for several checks!
>>
>>
>>    
>>
>>>+ public string consume_doc_comment ()
>>>+ {
>>>+ if (xml_comment_buffer.Length > 0) {
>>>+ string ret = null;
>>>+ if (xmlCommentSavePoint > 0) {
>>>+ ret = xml_comment_buffer.ToString (0, xmlCommentSavePoint);
>>>+ xml_comment_buffer.Remove (0, xmlCommentSavePoint);
>>>+ } else {
>>>+ ret = xml_comment_buffer.ToString ();
>>>+ xml_comment_buffer.Length = 0;
>>>+ }
>>>+ xmlCommentSavePoint = 0;
>>>+ return ret;
>>>+ }
>>>+ else
>>>+ return null;
>>>+ }
>>>
>>>The last else is useless and xmlCommentSavePoint = 0 should be only for
>>>xmlCommentSavePoint > 0.
>>>      
>>>
>>Ok. Actually I noticed that push_xml_comment() is not required anymore
>>(since I implemented invalid comment state transition), I just removed
>>it as well as xmlCommentSavePoint which is also not required anymore.
>>
>>
>>    
>>
>>>+ public enum XmlCommentState {
>>>+ OK,
>>>+ NG,
>>>+ Error
>>>+ }
>>>
>>>What is NG ?
>>>      
>>>
>>OK, I found that NG is kind of Japanglish ;-) so just renamed
>>OK as Allowed and NG as NotAllowed.
>>
>>
>>    
>>
>>>+ MemberInfo mi = FindDocumentedMember (type, memberName,
>>>parameterTypes, ds, out warnResult);
>>>+ switch (warnResult) {
>>>+ case 1581:
>>>+ Report.Warning (1581, 1, Location, "Invalid return type in XML comment
>>>cref attribute '{0}'", cref);
>>>+ return;
>>>+ case 1584:
>>>+ Report.Warning (1020, 1, Location, "Overloadable {0} operator is
>>>expected", parameterTypes.Length == 2 ? "binary" : "unary");
>>>+ Report.Warning (1584, 1, Location, "XML comment on '{0}' has
>>>syntactically incorrect attribute '{1}'", GetSignatureForError (), cref);
>>>+ return;
>>>+ }
>>>
>>>Why don't do it inside FindDocumentMember to avoid copy&paste.
>>>      
>>>
>>Fixed.
>>
>>
>>    
>>
>>>- : IDENTIFIER
>>>+ : IDENTIFIER
>>>+ {
>>>+ tmpComment = Lexer.consume_doc_comment ();
>>>+ Lexer.doc_state = XmlCommentState.OK;
>>>+ }
>>>OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS
>>>{
>>>oob_stack.Push (lexer.Location);
>>>
>>>- current_local_parameters = (Parameters) $3;
>>>+ current_local_parameters = (Parameters) $4;
>>>}
>>>opt_constructor_initializer
>>>{
>>>Location l = (Location) oob_stack.Pop ();
>>>- $$ = new Constructor (current_class, (string) $1, 0, (Parameters) $3,
>>>- (ConstructorInitializer) $6, l);
>>>+ $$ = new Constructor (current_class, (string) $1, 0, (Parameters) $4,
>>>+ (ConstructorInitializer) $7, l);
>>>}
>>>
>>>I am confuse, what has been changed.
>>>      
>>>
>>I cannot understand what you mean. It should not be regarded as
>>confusion, since it must happen whenever any of you guys have to
>>insert logic between tokens.
>>
>>
>>    
>>
>>>+ } catch (NotImplementedException ex) {
>>>+ Report.Error (1569, "Error generating XML documentation file '{0}'
>>>('{1}')", xml_documentation_file, ex.Message);
>>>+ return false;
>>>
>>>Does make a sense to catch this exception ?
>>>      
>>>
>>Oops, it should just be Exception (to catch xml-output errors).
>>
>>
>>    
>>
>>>+ vd.DocComment = ConsumeStoredComment ();
>>>+ Lexer.doc_state = XmlCommentState.OK;
>>>
>>>Why don't call this tricky property in ConsumeStroredComment I found
>>>only one place where this combination is missing (bug ??). Should be
>>>possible also to rename this property ?
>>>      
>>>
>>Made as such.
>>
>>
>>    
>>
>>>BTW: I think we should call this call only for /doc option.
>>>      
>>>
>>Ok, now everything in .jay checks RootContext.NeedDocument.
>>
>>I've attached the latest diff in mcs/mcs and mcs/tests, and added
>>new tests in mcs/errors (cs1587-*.cs). It still needs some love
>>on the files in tests (test-harness is not working properly; am
>>asking Hari about that).
>>
>>Atsushi Eno
>>    
>>
>
>  
>
>------------------------------------------------------------------------
>
>Index: cs-tokenizer.cs
>===================================================================
>--- cs-tokenizer.cs	(revision 36616)
>+++ cs-tokenizer.cs	(working copy)
>@@ -42,6 +42,17 @@
> 		bool handle_assembly = false;
> 
> 		//
>+		// XML documentation buffer. The save point is used to divide
>+		// comments on types and comments on members.
>+		//
>+		StringBuilder xml_comment_buffer;
>+
>+		//
>+		// See comment on XmlCommentState enumeration.
>+		//
>+		XmlCommentState xmlDocState = XmlCommentState.Allowed;
>+
>+		//
> 		// Whether tokens have been seen on this line
> 		//
> 		bool tokens_seen = false;
>@@ -132,6 +143,18 @@
> 				handle_remove_add = value;
> 			}
> 		}
>+
>+		public XmlCommentState doc_state {
>+			get { return xmlDocState; }
>+			set {
>+				if (value == XmlCommentState.Allowed) {
>+					check_incorrect_doc_comment ();
>+					consume_doc_comment ();
>+				}
>+				xmlDocState = value;
>+			}
>+		}
>+
> 		
> 		//
> 		// Class variables
>@@ -362,6 +385,8 @@
> 					define (def);
> 			}
> 
>+			xml_comment_buffer = new StringBuilder ();
>+
> 			//
> 			// FIXME: This could be `Location.Push' but we have to
> 			// find out why the MS compiler allows this
>@@ -411,6 +436,9 @@
> 			case '}':
> 				return Token.CLOSE_BRACE;
> 			case '[':
>+				// To block doccomment inside attribute declaration.
>+				if (doc_state == XmlCommentState.Allowed)
>+					doc_state = XmlCommentState.NotAllowed;
> 				return Token.OPEN_BRACKET;
> 			case ']':
> 				return Token.CLOSE_BRACKET;
>@@ -1741,6 +1769,15 @@
> 		{
> 			int res = consume_identifier (s, false);
> 
>+			if (doc_state == XmlCommentState.Allowed)
>+				doc_state = XmlCommentState.NotAllowed;
>+			switch (res) {
>+			case Token.USING:
>+			case Token.NAMESPACE:
>+				check_incorrect_doc_comment ();
>+				break;
>+			}
>+
> 			if (res == Token.PARTIAL) {
> 				// Save current position and parse next token.
> 				int old = reader.Position;
>@@ -1862,6 +1899,13 @@
> 				
> 					if (d == '/'){
> 						getChar ();
>+						if (RootContext.NeedDocument && peekChar () == '/') {
>+							getChar ();
>+							if (doc_state == XmlCommentState.Allowed)
>+								handle_one_line_xml_comment ();
>+							else if (doc_state == XmlCommentState.NotAllowed)
>+								warn_incorrect_doc_comment ();
>+						}
> 						while ((d = getChar ()) != -1 && (d != '\n') && d != '\r')
> 							col++;
> 						if (d == '\n'){
>@@ -1874,13 +1918,33 @@
> 						continue;
> 					} else if (d == '*'){
> 						getChar ();
>+						bool docAppend = false;
>+						if (RootContext.NeedDocument && peekChar () == '*') {
>+							getChar ();
>+							// But when it is /**/, just do nothing.
>+							if (peekChar () == '/') {
>+								getChar ();
>+								continue;
>+							}
>+							if (doc_state == XmlCommentState.Allowed)
>+								docAppend = true;
>+							else if (doc_state == XmlCommentState.NotAllowed)
>+								warn_incorrect_doc_comment ();
>+						}
> 
>+						int currentCommentStart = xml_comment_buffer.Length;
>+						if (docAppend)
>+							xml_comment_buffer.Append (Environment.NewLine);
>+
> 						while ((d = getChar ()) != -1){
> 							if (d == '*' && peekChar () == '/'){
> 								getChar ();
> 								col++;
> 								break;
> 							}
>+							if (docAppend)
>+								xml_comment_buffer.Append ((char) d);
>+							
> 							if (d == '\n'){
> 								line++;
> 								ref_line++;
>@@ -1889,6 +1953,8 @@
> 								tokens_seen = false;
> 							}
> 						}
>+						if (docAppend)
>+							update_formatted_doc_comment (currentCommentStart);
> 						continue;
> 					}
> 					goto is_punct_label;
>@@ -2037,6 +2103,97 @@
> 			return Token.EOF;
> 		}
> 
>+		//
>+		// Handles one line xml comment
>+		//
>+		private void handle_one_line_xml_comment ()
>+		{
>+			int c;
>+			while ((c = peekChar ()) == ' ')
>+				getChar (); // skip heading whitespaces.
>+			while ((c = peekChar ()) != -1 && (c != '\n') && c != '\r') {
>+				col++;
>+				xml_comment_buffer.Append ((char) getChar ());
>+			}
>+			if (c == '\r') {
>+				getChar ();
>+				xml_comment_buffer.Append ('\r');
>+				if (peekChar () == '\n')
>+					xml_comment_buffer.Append ('\n');
>+			}
>+			else if (c == '\n')
>+				xml_comment_buffer.Append ('\n');
>+		}
>+
>+		//
>+		// Remove heading "*" in Javadoc-like xml documentation.
>+		//
>+		private void update_formatted_doc_comment (int currentCommentStart)
>+		{
>+			int length = xml_comment_buffer.Length - currentCommentStart;
>+			string [] lines = xml_comment_buffer.ToString (
>+				currentCommentStart,
>+				length).Split ('\n');
>+			// The first line starts with /**, thus it is not target
>+			// for the format check.
>+			for (int i = 1; i < lines.Length; i++) {
>+				string s = lines [i];
>+				int idx = s.IndexOf ('*');
>+				string head = null;
>+				if (idx < 0) {
>+					if (i < lines.Length - 1)
>+						return;
>+					head = s;
>+				}
>+				else
>+					head = s.Substring (0, idx);
>+				foreach (char c in head)
>+					if (c != ' ')
>+						return;
>+				lines [i] = s.Substring (idx + 1);
>+			}
>+			xml_comment_buffer.Remove (currentCommentStart, length);
>+			xml_comment_buffer.Insert (
>+				currentCommentStart,
>+				String.Join ("\n", lines));
>+		}
>+
>+		//
>+		// Checks if there was incorrect doc comments and raise
>+		// warnings.
>+		//
>+		public void check_incorrect_doc_comment ()
>+		{
>+			if (xml_comment_buffer.Length > 0)
>+				warn_incorrect_doc_comment ();
>+		}
>+
>+		//
>+		// Raises a warning when tokenizer found incorrect doccomment
>+		// markup.
>+		//
>+		private void warn_incorrect_doc_comment ()
>+		{
>+			doc_state = XmlCommentState.Error;
>+			// in csc, it is 'XML comment is not placed on a valid 
>+			// language element'. But that does not make sense.
>+			Report.Warning (1587, 2, Location, "XML comment is placed on an invalid language element which can not accept it.");
>+		}
>+
>+		//
>+		// Consumes the saved xml comment lines (if any)
>+		// as for current target member or type.
>+		//
>+		public string consume_doc_comment ()
>+		{
>+			if (xml_comment_buffer.Length > 0) {
>+				string ret = xml_comment_buffer.ToString ();
>+				xml_comment_buffer.Length = 0;
>+				return ret;
>+			}
>+			return null;
>+		}
>+
> 		public void cleanup ()
> 		{
> 			if (ifstack != null && ifstack.Count >= 1) {
>@@ -2049,4 +2206,18 @@
> 				
> 		}
> 	}
>+
>+	//
>+	// Indicates whether it accepts XML documentation or not.
>+	//
>+	public enum XmlCommentState {
>+		// comment is allowed in this state.
>+		Allowed,
>+		// comment is not allowed in this state.
>+		NotAllowed,
>+		// once comments appeared when it is NotAllowed, then the
>+		// state is changed to it, until the state is changed to
>+		// .Allowed.
>+		Error
>+	}
> }
>Index: rootcontext.cs
>===================================================================
>--- rootcontext.cs	(revision 36616)
>+++ rootcontext.cs	(working copy)
>@@ -13,6 +13,7 @@
> using System.Reflection;
> using System.Reflection.Emit;
> using System.Diagnostics;
>+using System.Xml;
> 
> namespace Mono.CSharp {
> 
>@@ -80,12 +81,34 @@
> 		public static bool StrongNameDelaySign = false;
> 
> 		//
>+		// If set, enable XML documentation generation
>+		//
>+		public static bool NeedDocument = false;
>+
>+		//
>+		// Used to create element which helps well-formedness checking.
>+		//
>+		public static XmlDocument XmlDocumentation;
>+
>+		//
>+		// The output for XML documentation.
>+		//
>+		public static XmlWriter XmlCommentOutput;
>+
>+		//
>+		// Stores XmlDocuments that are included in XML documentation.
>+		//
>+		public static Hashtable StoredDocuments = new Hashtable ();
>+
>+		//
> 		// Constructor
> 		//
> 		static RootContext ()
> 		{
> 			tree = new Tree ();
> 			type_container_resolve_order = new ArrayList ();
>+			XmlDocumentation = new XmlDocument ();
>+			XmlDocumentation.PreserveWhitespace = false;
> 		}
> 		
> 		public static bool NeedsEntryPoint {
>@@ -174,6 +197,29 @@
> 					e.DefineType ();
> 		}
> 
>+		//
>+		// Fixes full type name of each documented types/members up.
>+		//
>+		static public void GenerateDocComment ()
>+		{
>+			TypeContainer root = Tree.Types;
>+
>+			if (root.Interfaces != null)
>+				foreach (Interface i in root.Interfaces) 
>+					i.GenerateDocComment (null);
>+
>+			foreach (TypeContainer tc in root.Types)
>+				tc.GenerateDocComment (null);
>+
>+			if (root.Delegates != null)
>+				foreach (Delegate d in root.Delegates) 
>+					d.GenerateDocComment (null);
>+
>+			if (root.Enums != null)
>+				foreach (Enum e in root.Enums)
>+					e.GenerateDocComment (null);
>+		}
>+
> 		static void Error_TypeConflict (string name, Location loc)
> 		{
> 			Report.Error (
>Index: class.cs
>===================================================================
>--- class.cs	(revision 36616)
>+++ class.cs	(working copy)
>@@ -40,6 +40,7 @@
> using System.Security;
> using System.Security.Permissions;
> using System.Text;
>+using System.Xml;
> 
> using Mono.CompilerServices.SymbolWriter;
> 
>@@ -2396,6 +2397,62 @@
> 			return FindMembers (mt, bf | BindingFlags.DeclaredOnly, null, null);
> 		}
> 
>+		//
>+		// Generates xml doc comments (if any), and if required,
>+		// handle warning report.
>+		//
>+		internal override void GenerateDocComment (DeclSpace ds)
>+		{
>+			base.GenerateDocComment (ds);
>+
>+			if (default_static_constructor != null)
>+				default_static_constructor.GenerateDocComment (this);
>+
>+			if (InstanceConstructors != null)
>+				foreach (Constructor c in InstanceConstructors)
>+					c.GenerateDocComment (this);
>+
>+			if (Types != null)
>+				foreach (TypeContainer tc in Types)
>+					tc.GenerateDocComment (this);
>+
>+			if (Enums != null)
>+				foreach (Enum en in Enums)
>+					en.GenerateDocComment (this);
>+
>+			if (Constants != null)
>+				foreach (Const c in Constants)
>+					c.GenerateDocComment (this);
>+
>+			if (Fields != null)
>+				foreach (Field f in Fields)
>+					f.GenerateDocComment (this);
>+
>+			if (Events != null)
>+				foreach (Event e in Events)
>+					e.GenerateDocComment (this);
>+
>+			if (Indexers != null)
>+				foreach (Indexer ix in Indexers)
>+					ix.GenerateDocComment (this);
>+
>+			if (Properties != null)
>+				foreach (Property p in Properties)
>+					p.GenerateDocComment (this);
>+
>+			if (Methods != null)
>+				foreach (Method m in Methods)
>+					m.GenerateDocComment (this);
>+
>+			if (Operators != null)
>+				foreach (Operator o in Operators)
>+					o.GenerateDocComment (this);
>+		}
>+
>+		public override string DocCommentHeader {
>+			get { return "T:"; }
>+		}
>+
> 		public virtual MemberCache ParentCache {
> 			get {
> 				return parent_cache;
>@@ -3296,6 +3353,72 @@
> 			return true;
> 		}
> 
>+		//
>+		// Returns a string that represents the signature for this 
>+		// member which should be used in XML documentation.
>+		//
>+		public override string GetDocCommentName (DeclSpace ds)
>+		{
>+			Parameter [] plist = Parameters.FixedParameters;
>+			Parameter parr = Parameters.ArrayParameter;
>+			string paramSpec = String.Empty;
>+			if (plist != null) {
>+				StringBuilder psb = new StringBuilder ();
>+				foreach (Parameter p in plist) {
>+					psb.Append (psb.Length != 0 ? "," : "(");
>+					psb.Append (p.ParameterType.FullName.Replace ("+", "."));
>+				}
>+				psb.Append (")");
>+				paramSpec = psb.ToString ();
>+			}
>+			else if (parr != null)
>+				paramSpec = String.Concat (
>+					"(",
>+					parr.ParameterType.FullName.Replace ("+", "."),
>+					")");
>+
>+			string name = this is Constructor ? "#ctor" : Name;
>+			return String.Concat (DocCommentHeader, ds.Name, ".", name, paramSpec);
>+		}
>+
>+		//
>+		// Raised (and passed an XmlElement that contains the comment)
>+		// when GenerateDocComment is writing documentation expectedly.
>+		//
>+		// FIXME: with a few effort, it could be done with XmlReader,
>+		// that means removal of DOM use.
>+		//
>+		internal override void OnGenerateDocComment (DeclSpace ds, XmlElement el)
>+		{
>+			Hashtable paramTags = new Hashtable ();
>+			foreach (XmlElement pelem in el.SelectNodes ("param")) {
>+				int i;
>+				string xname = pelem.GetAttribute ("name");
>+				if (xname == "")
>+					continue; // really? but MS looks doing so
>+				if (xname != "" && Parameters.GetParameterByName (xname, out i) == null)
>+					Report.Warning (1572, 2, Location, "XML comment on '{0}' has a 'param' tag for '{1}', but there is no such parameter.", Name, xname);
>+				else if (paramTags [xname] != null)
>+					Report.Warning (1571, 2, Location, "XML comment on '{0}' has a duplicate param tag for '{1}'", Name, xname);
>+				paramTags [xname] = xname;
>+			}
>+			Parameter [] plist = Parameters.FixedParameters;
>+			Parameter parr = Parameters.ArrayParameter;
>+			if (plist != null) {
>+				foreach (Parameter p in plist) {
>+					if (paramTags.Count > 0 && paramTags [p.Name] == null)
>+						Report.Warning (1573, 4, Location, "Parameter '{0}' has no matching param tag in the XML comment for '{1}' (but other parameters do)", Name, p.Name);
>+				}
>+			}
>+		}
>+
>+		//
>+		//   Represents header string for documentation comment.
>+		//
>+		public override string DocCommentHeader {
>+			get { return "M:"; }
>+		}
>+
> 		protected override void VerifyObsoleteAttribute()
> 		{
> 			base.VerifyObsoleteAttribute ();
>@@ -5223,6 +5346,13 @@
> 
> 			base.Emit ();
> 		}
>+
>+		//
>+		//   Represents header string for documentation comment.
>+		//
>+		public override string DocCommentHeader {
>+			get { return "F:"; }
>+		}
> 	}
> 
> 	//
>@@ -5531,6 +5661,13 @@
> 			}
> 		}
> 
>+		//
>+		//   Represents header string for documentation comment.
>+		//
>+		public override string DocCommentHeader {
>+			get { throw new InvalidOperationException ("Unexpected attempt to get doc comment from " + this.GetType () + "."); }
>+		}
>+
> 		protected override void VerifyObsoleteAttribute()
> 		{
> 		}
>@@ -5981,6 +6118,13 @@
> 				return attribute_targets;
> 			}
> 		}
>+
>+		//
>+		//   Represents header string for documentation comment.
>+		//
>+		public override string DocCommentHeader {
>+			get { return "P:"; }
>+		}
> 	}
> 			
> 	public class Property : PropertyBase, IIteratorContainer {
>@@ -6621,6 +6765,13 @@
> 
> 			return TypeManager.GetFullNameSignature (EventBuilder);
> 		}
>+
>+		//
>+		//   Represents header string for documentation comment.
>+		//
>+		public override string DocCommentHeader {
>+			get { return "E:"; }
>+		}
> 	}
> 
> 
>Index: decl.cs
>===================================================================
>--- decl.cs	(revision 36616)
>+++ decl.cs	(working copy)
>@@ -16,6 +16,7 @@
> using System.Globalization;
> using System.Reflection.Emit;
> using System.Reflection;
>+using System.Xml;
> 
> namespace Mono.CSharp {
> 
>@@ -137,6 +138,17 @@
> 		/// </summary>
> 		public readonly Location Location;
> 
>+		/// <summary>
>+		///   XML documentation comment
>+		/// </summary>
>+		public string DocComment;
>+
>+		/// <summary>
>+		///   Represents header string for documentation comment 
>+		///   for each member types.
>+		/// </summary>
>+		public abstract string DocCommentHeader { get; }
>+
> 		[Flags]
> 		public enum Flags {
> 			Obsolete_Undetected = 1,		// Obsolete attribute has not been detected yet
>@@ -363,6 +375,473 @@
> 
> 		protected abstract void VerifyObsoleteAttribute ();
> 
>+		//
>+		// Raised (and passed an XmlElement that contains the comment)
>+		// when GenerateDocComment is writing documentation expectedly.
>+		//
>+		internal virtual void OnGenerateDocComment (DeclSpace ds, XmlElement intermediateNode)
>+		{
>+		}
>+
>+		//
>+		// Returns a string that represents the signature for this 
>+		// member which should be used in XML documentation.
>+		//
>+		public virtual string GetDocCommentName (DeclSpace ds)
>+		{
>+			if (ds == null || this is DeclSpace)
>+				return DocCommentHeader + Name;
>+			else
>+				return String.Concat (DocCommentHeader, ds.Name, ".", Name);
>+		}
>+
>+		private XmlNode GetDocCommentNode (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.XmlDocumentation;
>+			try {
>+				XmlElement el = doc.CreateElement ("member");
>+				el.SetAttribute ("name", name);
>+				string normalized = 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 =
>+					DocComment.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 = String.Join (
>+					"\n            ", split, 0, j);
>+				return el;
>+			} catch (XmlException ex) {
>+				Report.Warning (1570, 1, 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 virtual void GenerateDocComment (DeclSpace ds)
>+		{
>+			if (DocComment != null) {
>+				string name = GetDocCommentName (ds);
>+
>+				XmlNode n = GetDocCommentNode (name);
>+
>+				XmlElement el = n as XmlElement;
>+				if (el != null) {
>+					OnGenerateDocComment (ds, el);
>+
>+					// FIXME: it could be done with XmlReader
>+					foreach (XmlElement inc in n.SelectNodes (".//include"))
>+						HandleInclude (inc);
>+
>+					// FIXME: it could be done with XmlReader
>+					DeclSpace dsTarget = this as DeclSpace;
>+					if (dsTarget == null)
>+						dsTarget = ds;
>+
>+					foreach (XmlElement see in n.SelectNodes (".//see"))
>+						HandleSee (dsTarget, name, see);
>+					foreach (XmlElement seealso in n.SelectNodes (".//seealso"))
>+						HandleSeeAlso (dsTarget, name, seealso);
>+				}
>+
>+				n.WriteTo (RootContext.XmlCommentOutput);
>+			}
>+			else if (IsExposedFromAssembly (ds) &&
>+				// There are no warnings when the container also
>+				// misses documentations.
>+				(ds == null || ds.DocComment != null))
>+			{
>+				Report.Warning (1591, 4, Location,
>+					"Missing XML comment for publicly visible type or member '{0}'", GetSignatureForError ());
>+			}
>+		}
>+
>+		//
>+		// Processes "include" element. Check included file and
>+		// embed the document content inside this documentation node.
>+		//
>+		private void HandleInclude (XmlElement el)
>+		{
>+			string file = el.GetAttribute ("file");
>+			string path = el.GetAttribute ("path");
>+			if (file == "") {
>+				Report.Warning (1590, 1, 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, Location, "Invalid XML 'include' element; Missing 'path' attribute.");
>+				el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Include tag is invalid "), el);
>+			}
>+			else {
>+				XmlDocument doc = RootContext.StoredDocuments [file] as XmlDocument;
>+				if (doc == null) {
>+					try {
>+						doc = new XmlDocument ();
>+						doc.Load (file);
>+						RootContext.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, 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, 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 void HandleSee (DeclSpace ds, string name, XmlElement see)
>+		{
>+			HandleXrefCommon (ds, name, see);
>+		}
>+
>+		//
>+		// Handles <seealso> elements.
>+		//
>+		private void HandleSeeAlso (DeclSpace ds, string name, XmlElement seealso)
>+		{
>+			HandleXrefCommon (ds, name, 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 Type FindDocumentedType (string identifier, DeclSpace ds)
>+		{
>+			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);
>+			}
>+			return ds.FindType (this.Location, identifier);
>+		}
>+
>+		//
>+		// Returns a MemberInfo that is referenced in XML documentation
>+		// (by "see" or "seealso" elements).
>+		//
>+		private MemberInfo FindDocumentedMember (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)
>+				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);
>+				return (mis.Length > 0) ? mis [0] : null;
>+			}
>+
>+			// 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, Location, "Overloadable {0} operator is expected", paramList.Length == 2 ? "binary" : "unary");
>+					Report.Warning (1584, 1, Location, "XML comment on '{0}' has syntactically incorrect attribute '{1}'", GetSignatureForError (), cref);
>+					return null;
>+				}
>+			}
>+			// here we still does not consider return type (to
>+			// detect CS1581 or CS1002+CS1584).
>+			msig = new MethodSignature (oper, null, paramList);
>+			mis = type.FindMembers (
>+				MemberTypes.Method,
>+				BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static,
>+				MethodSignature.method_signature_filter,
>+				msig);
>+			if (mis.Length == 0)
>+				return null; // CS1574
>+			MemberInfo mi = mis [0];
>+			Type expected = mi is MethodInfo ?
>+				((MethodInfo) mi).ReturnType :
>+				mi is PropertyInfo ?
>+				((PropertyInfo) mi).PropertyType :
>+				null;
>+			if (returnTypeName != null) {
>+				Type returnType = FindDocumentedType (returnTypeName, ds);
>+				if (returnType == null || returnType != expected) {
>+					warningType = 1581;
>+					Report.Warning (1581, 1, Location, "Invalid return type in XML comment cref attribute '{0}'", cref);
>+					return null;
>+				}
>+			}
>+			return mis [0];
>+		}
>+
>+		private Type [] emptyParamList = new Type [0];
>+
>+		//
>+		// Processes "see" or "seealso" elements.
>+		// Checks cref attribute.
>+		//
>+		private void HandleXrefCommon (DeclSpace ds, string name, XmlElement xref)
>+		{
>+			string cref = xref.GetAttribute ("cref").Trim (wsChars);
>+			// when, XmlReader, "if (cref == null)"
>+			if (!xref.HasAttribute ("cref"))
>+				return;
>+			if (cref.Length == 0)
>+				Report.Warning (1001, 1, Location, "Identifier expected");
>+				// ... and continue until CS1584.
>+
>+			string signature, identifier, parameters;
>+
>+			// strip 'T:' 'M:' 'F:' 'P:' 'E:' etc.
>+			// Here, MS ignores its member kind. No idea why.
>+			if (cref.Length > 2 && cref [1] == ':')
>+				signature = cref.Substring (2).Trim (wsChars);
>+			else
>+				signature = cref;
>+
>+			int parensPos = signature.IndexOf ('(');
>+			if (parensPos > 0 && signature [signature.Length - 1] == ')') {
>+				identifier = signature.Substring (0, parensPos).Trim (wsChars);
>+				parameters = signature.Substring (parensPos + 1, signature.Length - parensPos - 2);
>+			}
>+			else {
>+				identifier = signature;
>+				parameters = String.Empty;
>+			}
>+
>+			string alias = ds.LookupAlias (identifier);
>+			if (alias != null)
>+				identifier = alias;
>+
>+			// Check if identifier is valid.
>+			// This check is not necessary to mark as error, but
>+			// csc specially reports CS1584 for wrong identifiers.
>+			foreach (string nameElem in identifier.Split ('.')) {
>+				if (!Tokenizer.IsValidIdentifier (nameElem) && nameElem.IndexOf ("operator") < 0) {
>+					Report.Warning (1584, 1, Location, "XML comment on '{0}' has syntactically incorrect attribute '{1}'", GetSignatureForError (), cref);
>+					xref.SetAttribute ("cref", "!:" + signature);
>+					return;
>+				}
>+			}
>+
>+			// check if parameters are valid
>+			Type [] parameterTypes = emptyParamList;
>+			if (parameters.Length > 0) {
>+				string [] paramList = parameters.Split (',');
>+				ArrayList plist = new ArrayList ();
>+				for (int i = 0; i < paramList.Length; i++) {
>+					string paramTypeName = paramList [i].Trim (wsChars);
>+					alias = ds.LookupAlias (paramTypeName);
>+					if (alias != null)
>+						paramTypeName = alias;
>+					Type paramType = FindDocumentedType (paramTypeName, ds);
>+					if (paramType == null) {
>+						Report.Warning (1580, 1, Location, "Invalid type for parameter '{0}' in XML comment cref attribute '{1}'", i + 1, cref);
>+						return;
>+					}
>+					plist.Add (paramType);
>+				}
>+				parameterTypes = plist.ToArray (typeof (Type)) as Type [];
>+			}
>+
>+			Type type = FindDocumentedType (identifier, ds);
>+			if (type != null) {
>+				xref.SetAttribute ("cref", "T:" + type.FullName.Replace ("+", "."));
>+				return; // a type
>+			}
>+
>+			int period = identifier.LastIndexOf ('.');
>+			if (period > 0) {
>+				string typeName = identifier.Substring (0, period);
>+				string memberName = identifier.Substring (period + 1);
>+				type = FindDocumentedType (typeName, ds);
>+				int warnResult;
>+				if (type != null) {
>+					MemberInfo mi = FindDocumentedMember (type, memberName, parameterTypes, ds, out warnResult, cref);
>+					if (warnResult > 0)
>+						return;
>+					if (mi != null) {
>+						xref.SetAttribute ("cref", GetMemberDocHead (mi.MemberType) + type.FullName.Replace ("+", ".") + "." + memberName);
>+						return; // a member of a type
>+					}
>+				}
>+			}
>+			else {
>+				int warnResult;
>+				MemberInfo mi = FindDocumentedMember (ds.TypeBuilder, identifier, parameterTypes, ds, out warnResult, cref);
>+				if (warnResult > 0)
>+					return;
>+				if (mi != null) {
>+					xref.SetAttribute ("cref", GetMemberDocHead (mi.MemberType) + ds.TypeBuilder.FullName.Replace ("+", ".") + "." + identifier);
>+					return; // local member name
>+				}
>+			}
>+
>+			Report.Warning (1574, 1, Location, "XML comment on '{0}' has cref attribute '{1}' that could not be resolved in '{2}'.", GetSignatureForError (), cref, ds.GetSignatureForError ());
>+
>+			xref.SetAttribute ("cref", "!:" + identifier);
>+		}
>+
>+		//
>+		// Get a prefix from member type for XML documentation (used
>+		// to formalize cref target name).
>+		//
>+		static string GetMemberDocHead (MemberTypes type)
>+		{
>+			switch (type) {
>+			case MemberTypes.Constructor:
>+			case MemberTypes.Method:
>+				return "M:";
>+			case MemberTypes.Event:
>+				return "E:";
>+			case MemberTypes.Field:
>+				return "F:";
>+			case MemberTypes.NestedType:
>+			case MemberTypes.TypeInfo:
>+				return "T:";
>+			case MemberTypes.Property:
>+				return "P:";
>+			}
>+			return "!:";
>+		}
> 	}
> 
> 	/// <summary>
>@@ -1325,7 +1804,7 @@
> 			IDictionaryEnumerator it = parent.member_hash.GetEnumerator ();
> 			while (it.MoveNext ()) {
> 				hash [it.Key] = ((ArrayList) it.Value).Clone ();
>-                        }
>+			 }
>                                 
> 			return hash;
> 		}
>Index: delegate.cs
>===================================================================
>--- delegate.cs	(revision 36616)
>+++ delegate.cs	(working copy)
>@@ -643,6 +643,13 @@
> 			}
> 		}
> 
>+		//
>+		//   Represents header string for documentation comment.
>+		//
>+		public override string DocCommentHeader {
>+			get { return "T:"; }
>+		}
>+
> 		protected override void VerifyObsoleteAttribute()
> 		{
> 			CheckUsageOfObsoleteAttribute (ret_type);
>Index: cs-parser.jay
>===================================================================
>--- cs-parser.jay	(revision 36616)
>+++ cs-parser.jay	(working copy)
>@@ -81,6 +81,14 @@
> 		/// The current file.
> 		///
> 		SourceFile file;
>+
>+		///
>+		/// Temporary Xml documentation cache.
>+		/// For enum types, we need one more temporary store.
>+		///
>+		string tmpComment;
>+		string enumTypeComment;
>+
> 		
> 		
> 		/// Current attribute target
>@@ -284,7 +292,13 @@
> 	
> opt_EOF
> 	: /* empty */
>+	{
>+		Lexer.check_incorrect_doc_comment ();
>+	}
> 	| EOF
>+	{
>+		Lexer.check_incorrect_doc_comment ();
>+	}
> 	;
> 
> outer_declarations
>@@ -304,7 +318,15 @@
> 
> using_directive
> 	: using_alias_directive
>+	{
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+	}
> 	| using_namespace_directive
>+	{
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+	}
> 	;
> 
> using_alias_directive
>@@ -375,6 +397,10 @@
> 
> namespace_body
> 	: OPEN_BRACE
>+	  {
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+	  }
> 	  opt_using_directives
> 	  opt_namespace_member_declarations
> 	  CLOSE_BRACE
>@@ -491,6 +517,13 @@
> 			} else {
> 				$$ = new Attributes (sect);
> 			}
>+			if ($$ == null) {
>+				if (RootContext.NeedDocument) {
>+					Lexer.check_incorrect_doc_comment ();
>+					Lexer.doc_state =
>+						XmlCommentState.Allowed;
>+				}
>+			}
> 		} else {
> 			$$ = new Attributes (sect);
> 		}		
>@@ -746,9 +779,16 @@
> 		if ($7 != null)
> 			current_class.Bases = (ArrayList) $7;
> 
>+		if (RootContext.NeedDocument)
>+			current_class.DocComment = Lexer.consume_doc_comment ();
>+
> 		current_class.Register ();
> 	  }
> 	  struct_body
>+	  {
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+	  }
> 	  opt_semicolon
> 	  {
> 		$$ = current_class;
>@@ -762,7 +802,12 @@
> 	;
> 
> struct_body
>-	: OPEN_BRACE opt_struct_member_declarations CLOSE_BRACE
>+	: OPEN_BRACE
>+	  {
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+	  }
>+	  opt_struct_member_declarations CLOSE_BRACE
> 	;
> 
> opt_struct_member_declarations
>@@ -814,6 +859,10 @@
> 				(Expression) constant.expression_or_array_initializer, modflags, 
> 				(Attributes) $1, l);
> 
>+			if (RootContext.NeedDocument) {
>+				c.DocComment = Lexer.consume_doc_comment ();
>+				Lexer.doc_state = XmlCommentState.Allowed;
>+			}
> 			current_container.AddConstant (c);
> 		}
> 	  }
>@@ -866,6 +915,10 @@
> 						 var.expression_or_array_initializer, 
> 						 (Attributes) $1, l);
> 
>+			if (RootContext.NeedDocument) {
>+				field.DocComment = Lexer.consume_doc_comment ();
>+				Lexer.doc_state = XmlCommentState.Allowed;
>+			}
> 			current_container.AddField (field);
> 		}
> 	  }
>@@ -927,6 +980,8 @@
> method_declaration
> 	: method_header {
> 		iterator_container = (IIteratorContainer) $1;
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.NotAllowed;
> 	  }
> 	  method_body
> 	  {
>@@ -953,6 +1008,9 @@
> 
> 		current_local_parameters = null;
> 		iterator_container = null;
>+
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.Allowed;
> 	  }
> 	;
> 
>@@ -989,6 +1047,9 @@
> 
> 		current_local_parameters = (Parameters) $6;
> 
>+		if (RootContext.NeedDocument)
>+			method.DocComment = Lexer.consume_doc_comment ();
>+
> 		$$ = method;
> 	  }
> 	| opt_attributes
>@@ -1003,6 +1064,10 @@
> 					    (Attributes) $1, lexer.Location);
> 
> 		current_local_parameters = (Parameters) $6;
>+
>+		if (RootContext.NeedDocument)
>+			method.DocComment = Lexer.consume_doc_comment ();
>+
> 		$$ = method;
> 	  }
> 	| opt_attributes
>@@ -1020,6 +1085,10 @@
> 					    lexer.Location);
> 
> 		current_local_parameters = (Parameters) $7;
>+
>+		if (RootContext.NeedDocument)
>+			method.DocComment = Lexer.consume_doc_comment ();
>+
> 		$$ = method;
> 	  }
> 	;
>@@ -1163,7 +1232,12 @@
> property_declaration
> 	: opt_attributes
> 	  opt_modifiers
>-	  type namespace_or_type_name
>+	  type
>+	  namespace_or_type_name
>+	  {
>+		if (RootContext.NeedDocument)
>+			tmpComment = Lexer.consume_doc_comment ();
>+	  }
> 	  OPEN_BRACE 
> 	  {
> 		implicit_value_parameter_type = (Expression) $3;
>@@ -1181,11 +1255,11 @@
> 	  CLOSE_BRACE
> 	  { 
> 		Property prop;
>-		Pair pair = (Pair) $7;
>+		Pair pair = (Pair) $8;
> 		Accessor get_block = (Accessor) pair.First;
> 		Accessor set_block = (Accessor) pair.Second;
> 
>-		Location loc = (Location) $6;
>+		Location loc = (Location) $7;
> 		MemberName name = (MemberName) $4;
> 
> 		prop = new Property (current_class, (Expression) $3, (int) $2, false,
>@@ -1196,6 +1270,10 @@
> 		current_container.AddProperty (prop);
> 		implicit_value_parameter_type = null;
> 		iterator_container = null;
>+
>+		if (RootContext.NeedDocument)
>+			prop.DocComment = ConsumeStoredComment ();
>+
> 	  }
> 	;
> 
>@@ -1236,6 +1314,10 @@
> 		$$ = new Accessor ((ToplevelBlock) $5, (int) $2, (Attributes) $1, lexer.Location);
> 		current_local_parameters = null;
> 		lexer.PropertyParsing = true;
>+
>+		if (RootContext.NeedDocument)
>+			if (Lexer.doc_state == XmlCommentState.Error)
>+				Lexer.doc_state = XmlCommentState.NotAllowed;
> 	  }
> 	;
> 
>@@ -1273,6 +1355,10 @@
> 		$$ = new Accessor ((ToplevelBlock) $5, (int) $2, (Attributes) $1, lexer.Location);
> 		current_local_parameters = null;
> 		lexer.PropertyParsing = true;
>+
>+		if (RootContext.NeedDocument)
>+			if (Lexer.doc_state == XmlCommentState.Error)
>+				Lexer.doc_state = XmlCommentState.NotAllowed;
> 	  }
> 	;
> 
>@@ -1310,6 +1396,11 @@
> 	  {
> 		current_class.Bases = (ArrayList) $7;
> 
>+		if (RootContext.NeedDocument) {
>+			current_class.DocComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+		}
>+
> 		current_class.Register ();
> 	  }
> 	  interface_body 
>@@ -1470,6 +1561,8 @@
> 				  new MemberName (TypeContainer.DefaultIndexerName),
> 				  (int) $2, true, (Parameters) $6, (Attributes) $1,
> 				  info.Get, info.Set, lexer.Location);
>+		if (RootContext.NeedDocument)
>+			((Indexer) $$).DocComment = ConsumeStoredComment ();
> 	  }
> 	;
> 
>@@ -1493,6 +1586,9 @@
> 			new Parameters (param_list, null, decl.location),
> 			(ToplevelBlock) $5, (Attributes) $1, decl.location);
> 
>+		if (RootContext.NeedDocument)
>+			op.DocComment = ConsumeStoredComment ();
>+
> 		if (SimpleIteratorContainer.Simple.Yields)
> 			op.SetYields ();
> 
>@@ -1522,12 +1618,18 @@
> 			op = Operator.OpType.UnaryNegation;
> 
> 		Parameter [] pars = new Parameter [1];
>+		Expression type = (Expression) $5;
> 
>-		pars [0] = new Parameter ((Expression) $5, (string) $6, Parameter.Modifier.NONE, null);
>+		pars [0] = new Parameter (type, (string) $6, Parameter.Modifier.NONE, null);
> 
> 		current_local_parameters = new Parameters (pars, null, lexer.Location);
> 
>-		$$ = new OperatorDeclaration (op, (Expression) $1, (Expression) $5, (string) $6,
>+		if (RootContext.NeedDocument) {
>+			tmpComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+		}
>+
>+		$$ = new OperatorDeclaration (op, (Expression) $1, type, (string) $6,
> 					      null, null, lexer.Location);
> 	}
> 	| type OPERATOR overloadable_operator
>@@ -1536,18 +1638,26 @@
> 	  	type IDENTIFIER 
> 	  CLOSE_PARENS
>         {
>-	       CheckBinaryOperator ((Operator.OpType) $3);
>+		CheckBinaryOperator ((Operator.OpType) $3);
> 
>-	       Parameter [] pars = new Parameter [2];
>+		Parameter [] pars = new Parameter [2];
> 
>-	       pars [0] = new Parameter ((Expression) $5, (string) $6, Parameter.Modifier.NONE, null);
>-	       pars [1] = new Parameter ((Expression) $8, (string) $9, Parameter.Modifier.NONE, null);
>+		Expression typeL = (Expression) $5;
>+		Expression typeR = (Expression) $8;
> 
>+	       pars [0] = new Parameter (typeL, (string) $6, Parameter.Modifier.NONE, null);
>+	       pars [1] = new Parameter (typeR, (string) $9, Parameter.Modifier.NONE, null);
>+
> 	       current_local_parameters = new Parameters (pars, null, lexer.Location);
>+
>+		if (RootContext.NeedDocument) {
>+			tmpComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+		}
> 	       
> 	       $$ = new OperatorDeclaration ((Operator.OpType) $3, (Expression) $1, 
>-					     (Expression) $5, (string) $6,
>-					     (Expression) $8, (string) $9, lexer.Location);
>+					     typeL, (string) $6,
>+					     typeR, (string) $9, lexer.Location);
>         }
> 	| conversion_operator_declarator
> 	;
>@@ -1624,6 +1734,9 @@
> 		c.OptAttributes = (Attributes) $1;
> 		c.ModFlags = (int) $2;
> 	
>+		if (RootContext.NeedDocument)
>+			c.DocComment = ConsumeStoredComment ();
>+
> 		if (c.Name == current_container.Basename){
> 			if ((c.ModFlags & Modifiers.STATIC) != 0){
> 				if ((c.ModFlags & Modifiers.Accessibility) != 0){
>@@ -1657,22 +1770,30 @@
> 		current_container.AddConstructor (c);
> 
> 		current_local_parameters = null;
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.Allowed;
> 	  }
> 	;
> 
> constructor_declarator
>-	: IDENTIFIER 
>+	: IDENTIFIER
>+	  {
>+		if (RootContext.NeedDocument) {
>+			tmpComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+		}
>+	  }
> 	  OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS 
> 	  {
> 		oob_stack.Push (lexer.Location);
> 
>-		current_local_parameters = (Parameters) $3;
>+		current_local_parameters = (Parameters) $4;
> 	  }
> 	  opt_constructor_initializer
> 	  {
> 		Location l = (Location) oob_stack.Pop ();
>-		$$ = new Constructor (current_class, (string) $1, 0, (Parameters) $3,
>-				      (ConstructorInitializer) $6, l);
>+		$$ = new Constructor (current_class, (string) $1, 0, (Parameters) $4,
>+				      (ConstructorInitializer) $7, l);
> 	  }
> 	;
> 
>@@ -1708,9 +1829,16 @@
>         ;
>         
> destructor_declaration
>-	: opt_attributes opt_finalizer TILDE IDENTIFIER OPEN_PARENS CLOSE_PARENS block
>+	: opt_attributes opt_finalizer TILDE 
> 	  {
>-		if ((string) $4 != current_container.Basename){
>+		if (RootContext.NeedDocument) {
>+			tmpComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.NotAllowed;
>+		}
>+	  }
>+	  IDENTIFIER OPEN_PARENS CLOSE_PARENS block
>+	  {
>+		if ((string) $5 != current_container.Basename){
> 			Report.Error (574, lexer.Location, "Name of destructor must match name of class");
> 		} else if (!(current_container is Class)){
> 			Report.Error (575, lexer.Location, "Destructors are only allowed in class types");
>@@ -1734,8 +1862,10 @@
> 			Method d = new Destructor (
> 				current_class, TypeManager.system_void_expr, m, "Finalize", 
> 				new Parameters (null, null, l), (Attributes) $1, l);
>+			if (RootContext.NeedDocument)
>+				d.DocComment = ConsumeStoredComment ();
> 		  
>-			d.Block = (ToplevelBlock) $7;
>+			d.Block = (ToplevelBlock) $8;
> 			current_container.AddMethod (d);
> 		}
> 	  }
>@@ -1756,7 +1886,11 @@
> 				lexer.Location);
> 
> 			current_container.AddEvent (e);
>-				       
>+
>+			if (RootContext.NeedDocument) {
>+				e.DocComment = Lexer.consume_doc_comment ();
>+				Lexer.doc_state = XmlCommentState.Allowed;
>+			}
> 		}
> 	  }
> 	| opt_attributes
>@@ -1788,7 +1922,11 @@
> 				current_class, (Expression) $4, (int) $2, false, name, null,
> 				(Attributes) $1, (Accessor) pair.First, (Accessor) pair.Second,
> 				loc);
>-			
>+			if (RootContext.NeedDocument) {
>+				e.DocComment = Lexer.consume_doc_comment ();
>+				Lexer.doc_state = XmlCommentState.Allowed;
>+			}
>+
> 			current_container.AddEvent (e);
> 			implicit_value_parameter_type = null;
> 		}
>@@ -1800,6 +1938,9 @@
> 			Report.Error (71, lexer.Location, "Explicit implementation of events requires property syntax");
> 		else 
> 			Report.Error (71, lexer.Location, "Event declaration should use property syntax");
>+
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.Allowed;
> 	  }
> 	;
> 
>@@ -1910,6 +2051,8 @@
> 		indexer = new Indexer (current_class, decl.type, name,
> 				       (int) $2, false, decl.param_list, (Attributes) $1,
> 				       get_block, set_block, loc);
>+		if (RootContext.NeedDocument)
>+			indexer.DocComment = ConsumeStoredComment ();
> 
> 		current_container.AddIndexer (indexer);
> 		
>@@ -1929,6 +2072,10 @@
> 		} else if (pars.FixedParameters == null && pars.ArrayParameter == null){
> 			Report.Error (1551, lexer.Location, "Indexers must have at least one parameter");
> 		}
>+		if (RootContext.NeedDocument) {
>+			tmpComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+		}
> 
> 		$$ = new IndexerDeclaration ((Expression) $1, null, pars);
> 	  }
>@@ -1942,8 +2089,14 @@
> 		} else if (pars.FixedParameters == null && pars.ArrayParameter == null){
> 			Report.Error (1551, lexer.Location, "Indexers must have at least one parameter");
> 		}
>+
> 		MemberName name = (MemberName) $2;
> 		$$ = new IndexerDeclaration ((Expression) $1, name, pars);
>+
>+		if (RootContext.NeedDocument) {
>+			tmpComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+		}
> 	  }
> 	;
> 
>@@ -1951,7 +2104,10 @@
> 	: opt_attributes
> 	  opt_modifiers
> 	  ENUM IDENTIFIER 
>-	  opt_enum_base
>+	  opt_enum_base {
>+		if (RootContext.NeedDocument)
>+			enumTypeComment = Lexer.consume_doc_comment ();
>+	  }
> 	  enum_body
> 	  opt_semicolon
> 	  { 
>@@ -1961,10 +2117,14 @@
> 		Enum e = new Enum (current_namespace, current_container, (Expression) $5, (int) $2,
> 				   full_name, (Attributes) $1, enum_location);
> 		
>-		foreach (VariableDeclaration ev in (ArrayList) $6) {
>+		if (RootContext.NeedDocument)
>+			e.DocComment = enumTypeComment;
>+
>+		foreach (VariableDeclaration ev in (ArrayList) $7) {
> 			e.AddEnumMember (ev.identifier, 
> 					 (Expression) ev.expression_or_array_initializer,
>-					 ev.Location, ev.OptAttributes);
>+					 ev.Location, ev.OptAttributes,
>+					 ev.DocComment);
> 		}
> 
> 		string name = full_name.GetName ();
>@@ -1980,10 +2140,21 @@
> 	;
> 
> enum_body
>-	: OPEN_BRACE opt_enum_member_declarations CLOSE_BRACE
>+	: OPEN_BRACE
> 	  {
>-		$$ = $2;
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.Allowed;
> 	  }
>+	  opt_enum_member_declarations
>+	  {
>+	  	// here will be evaluated after CLOSE_BLACE is consumed.
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+	  }
>+	  CLOSE_BRACE
>+	  {
>+		$$ = $3;
>+	  }
> 	;
> 
> opt_enum_member_declarations
>@@ -2012,15 +2183,31 @@
> enum_member_declaration
> 	: opt_attributes IDENTIFIER 
> 	  {
>-		$$ = new VariableDeclaration ((string) $2, null, lexer.Location, (Attributes) $1);
>+		VariableDeclaration vd = new VariableDeclaration ((string) $2, null, lexer.Location, (Attributes) $1);
>+
>+		if (RootContext.NeedDocument) {
>+			vd.DocComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+		}
>+
>+		$$ = vd;
> 	  }
> 	| opt_attributes IDENTIFIER
> 	  {
>-		  $$ = lexer.Location;
>+		$$ = lexer.Location;
>+		if (RootContext.NeedDocument) {
>+			tmpComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.NotAllowed;
>+		}
> 	  }
>           ASSIGN expression
> 	  { 
>-		$$ = new VariableDeclaration ((string) $2, $5, lexer.Location, (Attributes) $1);
>+		VariableDeclaration vd = new VariableDeclaration ((string) $2, $5, lexer.Location, (Attributes) $1);
>+
>+		if (RootContext.NeedDocument)
>+			vd.DocComment = ConsumeStoredComment ();
>+
>+		$$ = vd;
> 	  }
> 	;
> 
>@@ -2036,6 +2223,11 @@
> 		Delegate del = new Delegate (current_namespace, current_container, (Expression) $4,
> 					     (int) $2, name, (Parameters) $7, (Attributes) $1, l);
> 
>+		if (RootContext.NeedDocument) {
>+			del.DocComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+		}
>+
> 		current_container.AddDelegate (del);
> 		RootContext.Tree.RecordDecl (name.GetName (true), del);
> 	  }	
>@@ -2052,6 +2244,11 @@
> 			TypeManager.system_void_expr, (int) $2, name,
> 			(Parameters) $7, (Attributes) $1, l);
> 
>+		if (RootContext.NeedDocument) {
>+			del.DocComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+		}
>+
> 		current_container.AddDelegate (del);
> 		RootContext.Tree.RecordDecl (name.GetName (true), del);
> 	  }
>@@ -3055,9 +3252,18 @@
> 			current_class.Bases = (ArrayList) $7;
> 		}
> 
>+		if (RootContext.NeedDocument) {
>+			current_class.DocComment = Lexer.consume_doc_comment ();
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+		}
>+
> 		current_class.Register ();
> 	  }
>-	  class_body 
>+	  class_body
>+	  {
>+		if (RootContext.NeedDocument)
>+			Lexer.doc_state = XmlCommentState.Allowed;
>+	  }
> 	  opt_semicolon 
> 	  {
> 		$$ = current_class;
>@@ -4138,6 +4344,7 @@
> 	public object expression_or_array_initializer;
> 	public Location Location;
> 	public Attributes OptAttributes;
>+	public string DocComment;
> 
> 	public VariableDeclaration (string id, object eoai, Location l, Attributes opt_attrs)
> 	{
>@@ -4482,5 +4689,13 @@
> 	CheckToken (1041, yyToken, "Identifier expected");
> }
> 
>+string ConsumeStoredComment ()
>+{
>+	string s = tmpComment;
>+	tmpComment = null;
>+	Lexer.doc_state = XmlCommentState.Allowed;
>+	return s;
>+}
>+
> /* end end end */
> }
>Index: ChangeLog
>===================================================================
>--- ChangeLog	(revision 36616)
>+++ ChangeLog	(working copy)
>@@ -1,3 +1,35 @@
>+2004-11-26  Atsushi Enomoto  <atsushi at ximian.com>
>+
>+	all things are for /doc support:
>+
>+	* driver.cs:
>+	  Handle /doc command line option.
>+	  Report error 2006 instead of 5 for missing file name for /doc.
>+	  Added call to RootContext.GenerateDocComment() after type resolution.
>+	* cs-tokenizer.cs:
>+	  Added support for picking up documentation (/// and /** ... */),
>+	  including a new XmlCommentState enumeration.
>+	* cs-parser.jay:
>+	  Added lines to fill Documentation element for field, constant,
>+	  property, indexer, method, constructor, destructor, operator, event
>+	  and class, struct, interface, delegate, enum.
>+	  Added lines to warn incorrect comment.
>+	* rootcontext.cs :
>+	  Added NeedDocument, XmlCommentOutput and XmlDocumentation (values
>+	  are passed only when /doc was specified).
>+	  Added StoredDocuments() to cache <include>d xml comment documents.
>+	  Added static GenerateDocComment().
>+	* decl.cs:
>+	  Added DocComment, DocCommentHeader, GenerateDocComment() and
>+	  OnGenerateDocComment() and some supporting private members for
>+	  /doc feature to MemberCore.
>+	* class.cs:
>+	  Added GenerateDocComment() on TypeContainer, MethodCore and Operator.
>+	* delegate.cs:
>+	  Added overriden DocCommentHeader.
>+	* enum.cs:
>+	  Added overriden DocCommentHeader and GenerateDocComment().
>+
> 2004-11-25  Ben Maurer  <bmaurer at ximian.com>
> 
> 	* rootcontext.cs (LookupType): Make sure to cache lookups that
>Index: driver.cs
>===================================================================
>--- driver.cs	(revision 36616)
>+++ driver.cs	(working copy)
>@@ -17,6 +17,7 @@
> 	using System.IO;
> 	using System.Text;
> 	using System.Globalization;
>+	using System.Xml;
> 	using System.Diagnostics;
> 
> 	public enum Target {
>@@ -98,6 +99,11 @@
> 		static Encoding encoding;
> 
> 		//
>+		// XML Comment documentation
>+		//
>+		static string xml_documentation_file;
>+
>+		//
> 		// Whether the user has specified a different encoder manually
> 		//
> 		static bool using_default_encoder = true;
>@@ -220,6 +226,7 @@
> 				"   -nostdlib[+|-]     Does not load core libraries\n" +
> 				"   -nowarn:W1[,W2]    Disables one or more warnings\n" + 
> 				"   -out:FNAME         Specifies output file\n" +
>+				"   -doc:XMLFILE         Generates xml documentation into specified file\n" +
> 				"   -pkg:P1[,Pn]       References packages P1..Pn\n" + 
> 				"   --expect-error X   Expect that error X will be encountered\n" +
> 				"   -recurse:SPEC      Recursively compiles the files in SPEC ([dir]/file)\n" + 
>@@ -1101,10 +1108,11 @@
> 			}
> 			case "/doc": {
> 				if (value == ""){
>-					Report.Error (5, arg + " requires an argument");
>+					Report.Error (2006, arg + " requires an argument");
> 					Environment.Exit (1);
> 				}
>-				// TODO handle the /doc argument to generate xml doc
>+				xml_documentation_file = value;
>+				RootContext.NeedDocument = true;
> 				return true;
> 			}
> 			case "/lib": {
>@@ -1536,6 +1544,7 @@
> 			if (timestamps)
> 				ShowTime ("Resolving tree");
> 			RootContext.ResolveTree ();
>+
> 			if (Report.Errors > 0)
> 				return false;
> 			if (timestamps)
>@@ -1546,6 +1555,35 @@
> 			RootContext.PopulateTypes ();
> 			RootContext.DefineTypes ();
> 			
>+			if (RootContext.NeedDocument) {
>+				XmlTextWriter w = null;
>+				try {
>+					w = new XmlTextWriter (xml_documentation_file, null);
>+					w.Indentation = 4;
>+					w.Formatting = Formatting.Indented;
>+					w.WriteStartDocument ();
>+					w.WriteStartElement ("doc");
>+					w.WriteStartElement ("assembly");
>+					w.WriteStartElement ("name");
>+					w.WriteString (Path.ChangeExtension (output_file, null));
>+					w.WriteEndElement (); // name
>+					w.WriteEndElement (); // assembly
>+					w.WriteStartElement ("members");
>+					RootContext.XmlCommentOutput = w;
>+					RootContext.GenerateDocComment ();
>+					w.WriteFullEndElement (); // members
>+					w.WriteEndElement ();
>+					w.WriteWhitespace (Environment.NewLine);
>+					w.WriteEndDocument ();
>+				} catch (Exception ex) {
>+					Report.Error (1569, "Error generating XML documentation file '{0}' ('{1}')", xml_documentation_file, ex.Message);
>+					return false;
>+				} finally {
>+					if (w != null)
>+						w.Close ();
>+				}
>+			}
>+
> 			TypeManager.InitCodeHelpers ();
> 
> 			//
>@@ -1735,7 +1773,6 @@
> #endif
> 			return (Report.Errors == 0);
> 		}
>-
> 	}
> 
> 	//
>Index: enum.cs
>===================================================================
>--- enum.cs	(revision 36616)
>+++ enum.cs	(working copy)
>@@ -14,6 +14,7 @@
> using System.Reflection;
> using System.Reflection.Emit;
> using System.Globalization;
>+using System.Xml;
> 
> namespace Mono.CSharp {
> 
>@@ -100,6 +101,10 @@
> 		protected override void VerifyObsoleteAttribute()
> 		{
> 		}
>+
>+		public override string DocCommentHeader {
>+			get { return "F:"; }
>+		}
> 	}
> 
> 	/// <summary>
>@@ -152,7 +157,7 @@
> 		///   Adds @name to the enumeration space, with @expr
> 		///   being its definition.  
> 		/// </summary>
>-		public void AddEnumMember (string name, Expression expr, Location loc, Attributes opt_attrs)
>+		public void AddEnumMember (string name, Expression expr, Location loc, Attributes opt_attrs, string documentation)
> 		{
> 			if (name == "value__") {
> 				Report.Error (76, loc, "An item in an enumeration can't have an identifier `value__'");
>@@ -160,6 +165,7 @@
> 			}
> 
> 			EnumMember em = new EnumMember (this, expr, name, loc, opt_attrs);
>+			em.DocComment = documentation;
> 			if (!AddToContainer (em, false, name, ""))
> 				return;
> 
>@@ -784,5 +790,25 @@
> 		{
> 			// UnderlyingType is never obsolete
> 		}
>+
>+		//
>+		// Generates xml doc comments (if any), and if required,
>+		// handle warning report.
>+		//
>+		internal override void GenerateDocComment (DeclSpace ds)
>+		{
>+			base.GenerateDocComment (ds);
>+			foreach (string name in ordered_enums) {
>+				MemberCore mc = GetDefinition (name);
>+				mc.GenerateDocComment (this);
>+			}
>+		}
>+
>+		//
>+		//   Represents header string for documentation comment.
>+		//
>+		public override string DocCommentHeader {
>+			get { return "T:"; }
>+		}
> 	}
> }
>  
>
>------------------------------------------------------------------------
>
>Index: harness.mk
>===================================================================
>--- harness.mk	(revision 36619)
>+++ harness.mk	(working copy)
>@@ -9,6 +9,8 @@
> MCS = MONO_PATH="$(topdir)/class/lib/$(PROFILE)$(PLATFORM_PATH_SEPARATOR)$$MONO_PATH" $(INTERNAL_MCS)
> endif
> 
>+XMLDOCDIFF = $(TEST_RUNTIME) ../xmldocdiff.exe
>+
> all-local $(STD_TARGETS:=-local):
> 
> %.res:
>@@ -20,8 +22,17 @@
> 	  if test -f $*.exe; then \
> 	    echo '*** $(TEST_RUNTIME) ./$*.exe' >> $$testlogfile ; \
> 	      if $(TEST_RUNTIME) -O=-all ./$*.exe >> $$testlogfile 2>&1 ; then \
>-		echo "PASS: $*" > $@ ; \
>-	        rm -f $$testlogfile ; \
>+	        if test -f $*.xml; then \
>+	          if $(XMLDOCDIFF) ../$*-ref.xml $*.xml >> $$testlogfile ; then \
>+	            echo "PASS: $*: xml comparison" > $@ ; \
>+	            rm -f $$testlogfile ; \
>+	          else \
>+	            echo "FAIL: $*: xml comparison" > $@ ; \
>+	          fi \
>+	        else \
>+	          echo "PASS: $*" > $@ ; \
>+	          rm -f $$testlogfile ; \
>+	        fi \
> 	      else \
> 		echo "Exit code: $$?" >> $$testlogfile ; \
> 		echo "FAIL: $*" > $@ ; \
>Index: Makefile
>===================================================================
>--- Makefile	(revision 36620)
>+++ Makefile	(working copy)
>@@ -26,7 +26,8 @@
> # He may also move some to TEST_EXCLUDE_net_2_0 if some of the merges are inappropriate for GMCS.
> #
> NEW_TEST_SOURCES_common = test-294 test-304 test-305 test-306 test-307 test-318 mtest-5-dll mtest-5-exe \
>-			test-319-dll test-319-exe test-320
>+			test-319-dll test-319-exe test-320 \
>+			$(TEST_SOURCES_XML)
> 
> #
> # Please do _not_ add any tests here - all new tests should go into NEW_TEST_SOURCES_common
>@@ -181,7 +182,7 @@
> endif
> 
> .PHONY: test-harness test-harness-run
>-test-harness:
>+test-harness: xmldocdiff.exe
> 	@$(MAKE) -s test-harness-run
> 
> exe_tests := $(filter %-exe, $(TEST_SOURCES))
>@@ -222,3 +223,22 @@
> 	$(INTERNAL_ILASM) /dll property-il.il
> 	$(CSCOMPILE) /r:property-il.dll property-main.cs /out:property-main.exe
> 	$(TEST_RUNTIME) property-main.exe
>+
>+
>+#
>+# Test for /doc option; need to compare result documentation files.
>+#
>+
>+TEST_SOURCES_XML = \
>+	xml-001 xml-002 xml-003 xml-004 xml-005 xml-006 xml-007 xml-008 xml-009 xml-010 \
>+	xml-011 xml-012 xml-013 xml-014 xml-015 xml-016 xml-017 xml-018 xml-019 xml-020 \
>+	xml-021 xml-022 xml-023 xml-024 xml-025 xml-026
>+
>+# currently no formalization on 'cref' attribute was found, so there are some
>+# differences between MS.NET and mono.
>+TEST_SOURCES_XML_PENDING = xml-027
>+
>+xml-doc-tests := $(filter xml-%, $(TEST_SOURCES))
>+
>+xmldocdiff.exe:
>+	$(CSCOMPILE) xmldocdiff.cs
>  
>
>------------------------------------------------------------------------
>
><?xml version="1.0"?>
><doc>
>    <assembly>
>        <name>xml-025</name>
>    </assembly>
>    <members>
>        <member name="T:Testing.Test">
>            <!-- No matching elements were found for the following include tag --><include file="../xml-025.inc" path="/foo"/>
>        </member>
>        <member name="F:Testing.Test.S1">
>            <root attr="is attribute allowed?">
>  includes XML markup.
>  <child>test</child>
>  <child>test2</child>
></root>
>        </member>
>        <member name="F:Testing.Test.S2">
>            <child>test</child><child>test2</child>
>        </member>
>        <member name="F:Testing.Test.S3">
>            <!-- Failed to insert some or all of included XML -->
>        </member>
>    </members>
></doc>
>  
>
>------------------------------------------------------------------------
>
>// Compiler options: -doc:xml-025.xml
>// Note that it could not be compiled to generate reference output as it is.
>// csc needs '\\' instead of '/' for file specification.
>
>namespace Testing
>{
>   /// <include file='../xml-025.inc' path='/foo' />
>   public class Test
>   {
>	public static void Main ()
>	{
>	}
>
>	/// <include file='../xml-025.inc' path='/root'/>
>	public string S1;
>
>	/// <include file='../xml-025.inc' path='/root/child'/>
>	public string S2;
>
>	/// <include file='../xml-025.inc' path='/root/@attr'/>
>	public string S3;
>   }
>}
>
>  
>
>------------------------------------------------------------------------
>
>using System;
>using System.Collections;
>using System.Xml;
>
>public class Test
>{
>	public class ComparisonException : Exception
>	{
>		public ComparisonException (string message)
>			: base (message)
>		{
>		}
>	}
>
>	static bool debug = false;
>	static bool error = false;
>
>	public static void Main (string [] args)
>	{
>		if (args.Length < 2) {
>			Console.Error.WriteLine ("Usage: xmldocdiff [reference_output.xml] [actual_output.xml]");
>			return;
>		}
>		if (args.Length > 2 && args [2].EndsWith ("-debug"))
>			debug = true;
>
>		try {
>			Run (args);
>		} catch (Exception ex) {
>			Console.WriteLine ("FAIL: " + args [1]);
>			throw ex;
>		}
>		Console.WriteLine ("PASS: " + args [1]);
>	}
>
>	private static void Run (string [] args)
>	{
>		XmlDocument doc1 = new XmlDocument ();
>		doc1.Load (args [0]);
>		XmlDocument doc2 = new XmlDocument ();
>		doc2.Load (args [1]);
>
>		XmlNodeList memberList1 = doc1.SelectNodes ("/doc/members/member");
>		XmlNodeList memberList2 = doc2.SelectNodes ("/doc/members/member");
>
>		Hashtable namedItems = new Hashtable ();
>
>		foreach (XmlElement el in memberList1)
>			namedItems.Add (el.GetAttribute ("name"), el);
>		foreach (XmlElement el2 in memberList2) {
>			string name = el2.GetAttribute ("name");
>			XmlElement el1 = namedItems [name] as XmlElement;
>			if (el1 == null) {
>				Report ("Extraneous element found. Name is '{0}'", name);
>				continue;
>			}
>			namedItems.Remove (name);
>
>/*
>			string el1xml = el1.InnerXml.Replace ("\r", "").Trim ();
>			string el2xml = el2.InnerXml.Replace ("\r", "").Trim ();
>			if (el1xml != el2xml)
>				Report (@"Member {0} contains different XML.
>---- Expected: {1}
>---- Actual  : {2}", name, el1xml, el2xml);
>*/
>			CompareNodes (el1, el2);
>
>		}
>		foreach (string name in namedItems.Keys)
>			Report ("Expected comment was not found. Name is {0}, XML is {1}", name, ((XmlElement) namedItems [name]).OuterXml);
>
>		// finally, check other nodes than members
>		doc1.SelectSingleNode ("/doc/members").RemoveAll ();
>		doc2.SelectSingleNode ("/doc/members").RemoveAll ();
>		string xml1 = doc1.OuterXml.Replace ("\r", "").Trim ();
>		string xml2 = doc2.OuterXml.Replace ("\r", "").Trim ();
>		if (xml1 != xml2)
>			Report (@"Either of doc, assembly, name, members elements  are different.
>doc1: {0}
>doc2: {1}", xml1, xml2);
>	}
>
>	private static void CompareNodes (XmlNode n1, XmlNode n2)
>	{
>		if (n2 == null) {
>			Report (@"Nodes does not exist:
>Node1: {0}", n1.OuterXml);
>			return;
>		}
>		if (n1.NodeType != n2.NodeType) {
>			Report (@"Nodes differ:
>Node1: {0}
>Node2: {1}", n1.OuterXml, n2.OuterXml);
>			return;
>		}
>		if (n1.Name != n2.Name) {
>			Report (@"Node names differ:
>Node1: {0}
>Node2: {1}", n1.OuterXml, n2.OuterXml);
>			return;
>		}
>		if (n1 is XmlElement) {
>			for (int i = 0; i < n1.Attributes.Count; i++)
>				CompareNodes (n1.Attributes [i],
>					n2.Attributes [i]);
>			for (int i = 0; i < n1.ChildNodes.Count; i++)
>				CompareNodes (n1.ChildNodes [i],
>					n2.ChildNodes [i]);
>		}
>		if (n1.NodeType != XmlNodeType.Comment && n1.Value != null) {
>			string v1 = n1.Value.Trim ();
>			string v2 = n2.Value.Trim ();
>			if (v1 != v2)
>				Report (@"Node values differ:
>Node1: {0}
>Node2: {1}", v1, v2);
>		}
>	}
>
>	static void Report (string format, params string [] args)
>	{
>		error = true;
>		if (debug)
>			Console.WriteLine (format, args);
>		else
>			throw new ComparisonException (String.Format (format, args));
>	}
>}
>
>  
>




More information about the Mono-devel-list mailing list