[Mono-list] embedded xml docs

Atsushi Eno atsushi@ximian.com
Mon, 24 May 2004 22:40:20 +0900


This is a multi-part message in MIME format.
--------------090505040603060507090700
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Yuri Leikind wrote:
> Hello all,
> 
> I use Mono 0.31 packages from Debian Unstable.
> 
> I found out that the /doc option in mcs does not work. 
> I searched in mailing list archives and found out
> that this feature is not implemented, partially to the different
> way the Mono project chose for its documentation - storing
> docs in separate files and viewing help with Monodoc.
> 
> Ok. I use Monodoc. 
> 
> But how do I extract documentation from a source file using 
> Mono, if there is a way?

Hello,

You can try this patch for the latest mcs (the code should not be
changed, but not sure). Am wondering if we had better apply it now
that immediate before beta2.

Atsushi Eno

--------------090505040603060507090700
Content-Type: text/plain;
 name="mcs-doc-20040524.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="mcs-doc-20040524.diff"

? gc.log
? mcs-doc-20040421.diff
? mcs-doc-20040511.diff
? mcs-doc-20040524.diff
? mcs-testing.xml
? old-patches
? profile-gen-doc.txt
? profile-no-doc.txt
? profile-with-doc.txt
Index: ChangeLog
===================================================================
RCS file: /cvs/public/mcs/mcs/ChangeLog,v
retrieving revision 1.1517
diff -u -r1.1517 ChangeLog
--- ChangeLog	24 May 2004 10:37:00 -0000	1.1517
+++ ChangeLog	24 May 2004 12:42:23 -0000
@@ -1,3 +1,30 @@
+2004-05-24  Atsushi Enomoto  <atsushi@ximian.com>
+
+	all things are for /doc support:
+
+	* driver.cs:
+	  Added command line option --document (same as /doc).
+	  Report error 2006 instead of 5 for missing file name for /doc.
+	  Added call to RootContext.FixupDocument() after its tree resolution.
+	* rootcontext.cs :
+	  Added NeedDocument and XmlDocumentation (values are passed only 
+	  when /doc or --document was specified).
+	  Added static FixupDocument().
+	* decl.cs: Added Documentation element field for MemberCore.
+	* class.cs:
+	  Added virtual DeclSpace.FixupDocument().
+	  Added overriden FixupDocument() for TypeContainer.
+	  Added virtual MemberBase.FixupDocument().
+	  Added overriden FixupDocument() for MethodCore and Operator.
+	* 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 (it is far from complete).
+	* cs-tokenizer.cs:
+	  Added support for picking up documentation.
+	  (for ///, and roughly for /** .. */)
+
 2004-05-24  Raja R Harinath  <rharinath@novell.com>
 
 	* Makefile: Simplify.  Use executable.make.
Index: class.cs
===================================================================
RCS file: /cvs/public/mcs/mcs/class.cs,v
retrieving revision 1.442
diff -u -r1.442 class.cs
--- class.cs	23 May 2004 18:20:09 -0000	1.442
+++ class.cs	24 May 2004 12:42:24 -0000
@@ -2310,8 +2310,38 @@
 							"not override Object.GetHashCode ()");
 			}
 		}
-		
-		
+
+		public override void FixupDocument ()
+		{
+			if (default_static_constructor != null)
+				default_static_constructor.FixupDocument (this);
+			
+			if (methods != null)
+				foreach (Method m in methods)
+					m.FixupDocument (this);
+
+			if (operators != null)
+				foreach (Operator o in operators)
+					o.FixupDocument (this);
+
+			if (properties != null)
+				foreach (Property p in properties)
+					p.FixupDocument (this);
+
+			if (indexers != null){
+				foreach (Indexer ix in indexers)
+					ix.FixupDocument (this);
+			}
+			
+			if (fields != null)
+				foreach (Field f in fields)
+					f.FixupDocument (this);
+
+			if (events != null){
+				foreach (Event e in Events)
+					e.FixupDocument (this);
+			}
+		}
 	}
 
 	public class ClassOrStruct : TypeContainer {
@@ -2666,6 +2696,26 @@
 			
 			return cc;
 		}
+
+		public override void FixupDocument (TypeContainer container)
+		{
+			if (Documentation != null) {
+				string paramSpec = null;
+				Parameter [] plist = Parameters.FixedParameters;
+				if (plist != null) {
+					foreach (Parameter p in plist)
+						paramSpec += (paramSpec != null ? "," : "(") + container.ResolveType (p.TypeName, false, Location).FullName;
+				}
+
+				if (paramSpec != null)
+					paramSpec += ")";
+				string head = this is PropertyBase ? "P:" : "M:";
+				string name = this is Constructor ? "#ctor" : Name;
+				Documentation.SetAttribute ("name", head + container.Name + "." + name + paramSpec);
+
+				Documentation = null; // release the reference
+			}
+		}
 	}
 
 	public class Method : MethodCore, IIteratorContainer, IMethodData {
@@ -4384,7 +4434,14 @@
 			return false;
 		}
 
-
+		/// <summary>
+		///   Fixups XML documentation type references
+		/// </summary>
+		public virtual void FixupDocument (TypeContainer container)
+		{
+			if (Documentation != null)
+				Documentation = null; // release the reference
+		}
 	}
 
 	//
@@ -6254,6 +6311,17 @@
 			
 			OperatorMethod.Emit (container);
 			Block = null;
+		}
+
+		public override void FixupDocument (TypeContainer container)
+		{
+			if (Documentation != null) {
+				string para1 = container.ResolveType (FirstArgType, false, Location).FullName;
+				string para2 = SecondArgType == null ? null : container.ResolveType (SecondArgType, false, Location).FullName;
+				Documentation.SetAttribute ("name", "M:" + container.Name + "." + Name + "(" + para1 + (para2 != null ? "," + para2 : null) + ")");
+
+				Documentation = null; // release the reference
+			}
 		}
 
 		public static string GetName (OpType ot)
Index: cs-parser.jay
===================================================================
RCS file: /cvs/public/mcs/mcs/cs-parser.jay,v
retrieving revision 1.298
diff -u -r1.298 cs-parser.jay
--- cs-parser.jay	21 May 2004 12:15:22 -0000	1.298
+++ cs-parser.jay	24 May 2004 12:42:25 -0000
@@ -18,6 +18,7 @@
 //   Run memory profiler with parsing only, and consider dropping 
 //   arraylists where not needed.   Some pieces can use linked lists.
 //
+using System.Xml;
 using System.Text;
 using System.IO;
 using System;
@@ -90,6 +91,12 @@
 		/// The current file.
 		///
 		SourceFile file;
+
+		///
+		/// Temporary Xml documentation cache
+		///
+		XmlElement tmpComment;
+
 		
 		
 		/// Current attribute target
@@ -339,6 +346,9 @@
 namespace_declaration
 	: opt_attributes NAMESPACE namespace_or_type_name
 	{
+		if (Lexer.xml_comment_buffer.Length > 0)
+			WarnIncorrectXmlComment ("namespace declaration");
+
 		if ($1 != null) {
 			Report.Error(1518, Lexer.Location, "Attributes cannot be applied to namespaces."
 					+ " Expected class, delegate, enum, interface, or struct");
@@ -712,6 +722,9 @@
 					 (int) $2, (Attributes) $1, lexer.Location);
 		current_container = new_struct;
 		RootContext.Tree.RecordDecl (full_struct_name, new_struct);
+
+		if (Lexer.xml_comment_buffer.Length > 0)
+			new_struct.Documentation = CreateMemberComment ("T:" + full_struct_name);
 	  }
 	  opt_class_base
 	  struct_body
@@ -780,6 +793,9 @@
 				(Attributes) $1, l);
 
 			CheckDef (current_container.AddConstant (c), c.Name, l);
+
+			if (Lexer.xml_comment_buffer.Length > 0)
+				c.Documentation = CreateMemberComment ("F:" + MakeName (c.Name));
 		}
 	  }
 	;
@@ -824,6 +840,8 @@
 						 (Attributes) $1, l);
 
 			CheckDef (current_container.AddField (field), field.Name, l);
+			if (Lexer.xml_comment_buffer.Length > 0)
+				field.Documentation = CreateMemberComment ("F:" + MakeName (field.Name));
 		}
 	  }
 	| opt_attributes
@@ -940,6 +958,9 @@
 
 		current_local_parameters = (Parameters) $6;
 
+		if (Lexer.xml_comment_buffer.Length > 0)
+			method.Documentation = CreateMemberComment ("M:" + MakeName ((string) $4));
+
 		$$ = method;
 	  }
 	| opt_attributes
@@ -953,6 +974,10 @@
 					    (Attributes) $1, lexer.Location);
 
 		current_local_parameters = (Parameters) $6;
+
+		if (Lexer.xml_comment_buffer.Length > 0)
+			method.Documentation = CreateMemberComment ("M:" + MakeName ((string) $4));
+
 		$$ = method;
 	  }
 	| opt_attributes
@@ -1080,6 +1105,9 @@
 		$$ = lexer.Location;
 
 		iterator_container = SimpleIteratorContainer.GetSimple ();
+
+		if (Lexer.xml_comment_buffer.Length > 0)
+			tmpComment = CreateMemberComment ("P:" + MakeName ((string) $4));
 	  }
 	  accessor_declarations 
 	  {
@@ -1097,10 +1125,14 @@
 				     (string) $4, (Attributes) $1, get_block, set_block, loc);
 		if (SimpleIteratorContainer.Simple.Yields)
 			prop.SetYields ();
-		
 		CheckDef (current_container.AddProperty (prop), prop.Name, loc);
 		implicit_value_parameter_type = null;
 		iterator_container = null;
+
+		prop.Documentation = tmpComment;
+
+		if (Lexer.xml_comment_buffer.Length > 0)
+			WarnIncorrectXmlComment ("property definition body");
 	  }
 	;
 
@@ -1203,6 +1235,8 @@
 		current_interface = new_interface;
 		current_container = new_interface;
 		RootContext.Tree.RecordDecl (full_interface_name, new_interface);
+		if (Lexer.xml_comment_buffer.Length > 0)
+			new_interface.Documentation = CreateMemberComment ("T:" + full_interface_name);
 	  }
 	  opt_class_base
 	  interface_body opt_semicolon
@@ -1358,9 +1392,11 @@
 	  {
 		InterfaceAccessorInfo info = (InterfaceAccessorInfo) $10;
 
-		$$ = new Indexer (current_container, (Expression) $3, null, (int) $2, true,
+		Indexer indexer = new Indexer (current_container, (Expression) $3, null, (int) $2, true,
 				  (Parameters) $6, (Attributes) $1, info.Get, info.Set,
 				  lexer.Location);
+		indexer.Documentation = tmpComment;
+		$$ = indexer;
 	  }
 	;
 
@@ -1375,6 +1411,7 @@
 		
 		Operator op = new Operator (decl.optype, decl.ret_type, (int) $2, decl.arg1type, decl.arg1name,
 					    decl.arg2type, decl.arg2name, (Block) $5, (Attributes) $1, decl.location);
+		op.Documentation = tmpComment;
 
 		if (SimpleIteratorContainer.Simple.Yields)
 			op.SetYields ();
@@ -1405,12 +1442,16 @@
 			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 (Lexer.xml_comment_buffer.Length > 0)
+			tmpComment = CreateMemberComment ("M:" + MakeName ("op_" + op) + "(" + type +")");
+
+		$$ = new OperatorDeclaration (op, (Expression) $1, type, (string) $6,
 					      null, null, lexer.Location);
 	}
 	| type OPERATOR overloadable_operator
@@ -1419,14 +1460,22 @@
 	  	type IDENTIFIER 
 	  CLOSE_PARENS
         {
-	       CheckBinaryOperator ((Operator.OpType) $3);
+		Operator.OpType op = (Operator.OpType) $3;
+
+	       CheckBinaryOperator (op);
 
 	       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 (Lexer.xml_comment_buffer.Length > 0)
+			tmpComment = CreateMemberComment ("M:" + MakeName ("op_" + op) + "(" + MakeName (typeL.ToString ()) +"," + MakeName (typeR.ToString ()) + ")");
 	       
 	       $$ = new OperatorDeclaration ((Operator.OpType) $3, (Expression) $1, 
 					     (Expression) $5, (string) $6,
@@ -1507,6 +1556,9 @@
 		c.OptAttributes = (Attributes) $1;
 		c.ModFlags = (int) $2;
 	
+		if (Lexer.xml_comment_buffer.Length > 0)
+			c.Documentation = CreateMemberComment ("M:" + MakeName ("#ctor"));
+
 		if (c.Name == current_container.Basename){
 			if ((c.ModFlags & Modifiers.STATIC) != 0){
 				if ((c.ModFlags & Modifiers.Accessibility) != 0){
@@ -1617,6 +1669,8 @@
 			Method d = new Destructor (
 				current_container, TypeManager.system_void_expr, m, "Finalize", 
 				new Parameters (null, null, l), (Attributes) $1, l);
+			if (Lexer.xml_comment_buffer.Length > 0)
+				d.Documentation = CreateMemberComment ("M:" + MakeName ("Finalize"));
 		  
 			d.Block = (Block) $7;
 			CheckDef (current_container.AddMethod (d), d.Name, d.Location);
@@ -1637,6 +1691,8 @@
 
 			CheckDef (current_container.AddEvent (e), e.Name, e.Location);
 				       
+			if (Lexer.xml_comment_buffer.Length > 0)
+				e.Documentation = CreateMemberComment ("E:" + MakeName (e.Name));
 		}
 	  }
 	| opt_attributes
@@ -1665,6 +1721,9 @@
 			Event e = new EventProperty (current_container, (Expression) $4, (int) $2, false, (string) $5,
 					     null, (Attributes) $1, (Accessor) pair.First, (Accessor) pair.Second,
 					     loc);
+
+			if (Lexer.xml_comment_buffer.Length > 0)
+				e.Documentation = CreateMemberComment ("E:" + MakeName ((string) $5));
 			
 			CheckDef (current_container.AddEvent (e), e.Name, loc);
 			implicit_value_parameter_type = null;
@@ -1677,6 +1736,10 @@
 			Report.Error (71, lexer.Location, "Explicit implementation of events requires property syntax");
 		else 
 			Report.Error (71, lexer.Location, "Event declaration should use property syntax");
+
+		// FIXME: where to attach? (or is it allowed?)
+		if (Lexer.xml_comment_buffer.Length > 0)
+			CreateMemberComment ("E:" + MakeName ((string) $5));
 	  }
 	;
 
@@ -1774,6 +1837,7 @@
 		indexer = new Indexer (current_container, decl.type, decl.interface_type,
 				       (int) $2, false, decl.param_list, (Attributes) $1,
 				       get_block, set_block, loc);
+		indexer.Documentation = tmpComment;
 
 		// Note that there is no equivalent of CheckDef for this case
 		// We shall handle this in semantic analysis
@@ -1794,6 +1858,10 @@
 		if (pars.FixedParameters == null && pars.ArrayParameter == null){
 			Report.Error (1551, lexer.Location, "Indexers must have at least one parameter");
 		}
+		// Parameters will be fixed up after RootContext.ResolveType
+		if (Lexer.xml_comment_buffer.Length > 0)
+			// missing arglist is going to be filled later
+			tmpComment = CreateMemberComment ("P:" + MakeName ("Item"));
 
 		$$ = new IndexerDeclaration ((Expression) $1, null, pars);
 	  }
@@ -1804,6 +1872,10 @@
 		if (pars.FixedParameters == null && pars.ArrayParameter == null){
 			Report.Error (1551, lexer.Location, "Indexers must have at least one parameter");
 		}
+
+		if (Lexer.xml_comment_buffer.Length > 0)
+			tmpComment = CreateMemberComment ("P:" + MakeName ("Item"));
+
 		$$ = new IndexerDeclaration ((Expression) $1, $2.ToString (), pars);
 	  }
 	;
@@ -1822,6 +1894,9 @@
 		Enum e = new Enum (current_namespace, current_container, (Expression) $5, (int) $2,
 				   full_name, (Attributes) $1, enum_location);
 		
+		if (Lexer.xml_comment_buffer.Length > 0)
+			e.Documentation = CreateMemberComment ("T:" + full_name);
+
 		foreach (VariableDeclaration ev in (ArrayList) $6) {
 			Location loc = (Location) ev.Location;
 
@@ -1897,9 +1972,14 @@
 	  SEMICOLON
 	  {
 		Location l = lexer.Location;
+		string full_delegate_name = MakeName ((string) $5);
+
 		Delegate del = new Delegate (current_namespace, current_container, (Expression) $4,
-					     (int) $2, MakeName ((string) $5), (Parameters) $7, 
+					     (int) $2, full_delegate_name, (Parameters) $7, 
 					     (Attributes) $1, l);
+
+		if (Lexer.xml_comment_buffer.Length > 0)
+			del.Documentation = CreateMemberComment ("T:" + full_delegate_name);
 		  
 		CheckDef (current_container.AddDelegate (del), del.Name, l);
 	  }	
@@ -1912,11 +1992,16 @@
 	  SEMICOLON
 	  {
 		Location l = lexer.Location;
+		string full_delegate_name = MakeName ((string) $5);
+
 		Delegate del = new Delegate (
 			current_namespace, current_container,
-			TypeManager.system_void_expr, (int) $2, MakeName ((string) $5), 
+			TypeManager.system_void_expr, (int) $2, full_delegate_name, 
 			(Parameters) $7, (Attributes) $1, l);
 
+		if (Lexer.xml_comment_buffer.Length > 0)
+			del.Documentation = CreateMemberComment ("T:" + full_delegate_name);
+
 		CheckDef (current_container.AddDelegate (del), del.Name, l);
 	  }
 	;
@@ -2862,6 +2947,9 @@
 				       (Attributes) $1, lexer.Location);
 		current_container = new_class;
 		RootContext.Tree.RecordDecl (name, new_class);
+
+		if (Lexer.xml_comment_buffer.Length > 0)
+			new_class.Documentation = CreateMemberComment ("T:" + name);
 	  }
 	  opt_class_base
 	  class_body 
@@ -4299,6 +4387,34 @@
 void CheckIdentifierToken (int yyToken)
 {
 	CheckToken (1041, yyToken, "Identifier expected");
+}
+
+XmlElement CreateMemberComment (string fullyQualifiedName)
+{
+	XmlDocument doc = RootContext.XmlDocumentation;
+	if (doc == null) {
+		RootContext.XmlDocumentation = doc = new XmlDocument ();
+		doc.AppendChild (doc.CreateElement ("doc"));
+		doc.DocumentElement.AppendChild (doc.CreateElement ("members"));
+	}
+	XmlElement el = null;
+	try {
+		el = doc.CreateElement ("member");
+		el.InnerXml = Lexer.ConsumeXmlComment ();
+		el.SetAttribute ("name", fullyQualifiedName);
+		doc.DocumentElement.LastChild.AppendChild (el);
+	} catch (XmlException ex) {
+		Report.Warning (1570, Lexer.Location, "XML comment is not well-formed: " + ex.Message);
+		XmlComment com = doc.CreateComment ("FIXME: Invalid documentation markup was found for member " + fullyQualifiedName);
+		doc.DocumentElement.LastChild.AppendChild (com);
+	}
+	return el;
+}
+
+void WarnIncorrectXmlComment (string token_type)
+{
+	Report.Warning (1587, Lexer.Location, "XML comment is not acceptable on  " + token_type);
+	Lexer.ConsumeXmlComment ();
 }
 
 /* end end end */
Index: cs-tokenizer.cs
===================================================================
RCS file: /cvs/public/mcs/mcs/cs-tokenizer.cs,v
retrieving revision 1.109
diff -u -r1.109 cs-tokenizer.cs
--- cs-tokenizer.cs	10 Apr 2004 18:56:55 -0000	1.109
+++ cs-tokenizer.cs	24 May 2004 12:42:25 -0000
@@ -40,6 +40,8 @@
 		bool handle_get_set = false;
 		bool handle_remove_add = false;
 		bool handle_assembly = false;
+		public StringBuilder xml_comment_buffer;
+		int xmlCommentSavePoint;
 
 		//
 		// Whether tokens have been seen on this line
@@ -132,7 +134,21 @@
 				handle_remove_add = value;
 			}
 		}
-		
+
+		public string ConsumeXmlComment ()
+		{
+			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;
+		}
+
 		//
 		// Class variables
 		// 
@@ -359,6 +375,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
@@ -1703,8 +1721,18 @@
 			//
 			if (s >= 'a'){
 				int keyword = GetKeyword (id_builder, pos);
-				if (keyword != -1 && !quoted)
+				if (keyword != -1 && !quoted) {
+					switch (keyword) {
+					case Token.CLASS:
+					case Token.STRUCT:
+					case Token.INTERFACE:
+					case Token.ENUM:
+					case Token.DELEGATE:
+						push_xml_comment ();
+						break;
+					}
 					return keyword;
+				}
 			}
 
 			//
@@ -1769,6 +1797,10 @@
 				
 					if (d == '/'){
 						getChar ();
+						if (RootContext.NeedDocument && peekChar () == '/') {
+							getChar ();
+							handle_xml_comment ();
+						}
 						while ((d = getChar ()) != -1 && (d != '\n') && d != '\r')
 							col++;
 						if (d == '\n'){
@@ -1781,6 +1813,16 @@
 						continue;
 					} else if (d == '*'){
 						getChar ();
+						bool docAppend = false;
+						if (RootContext.NeedDocument && peekChar () == '*') {
+							getChar ();
+							// But when it is /**/, just do nothing.
+							if (peekChar () == '/') {
+								getChar ();
+								continue;
+							}
+							docAppend = true;
+						}
 
 						while ((d = getChar ()) != -1){
 							if (d == '*' && peekChar () == '/'){
@@ -1788,6 +1830,9 @@
 								col++;
 								break;
 							}
+							if (docAppend)
+								xml_comment_buffer.Append ((char) d);
+							
 							if (d == '\n'){
 								line++;
 								ref_line++;
@@ -1942,6 +1987,24 @@
 			}
 
 			return Token.EOF;
+		}
+
+		private void handle_xml_comment ()
+		{
+			int c;
+			while ((c = peekChar ()) != -1 && (c != '\n') && c != '\r') {
+				col++;
+				xml_comment_buffer.Append ((char) getChar ());
+			}
+		}
+
+		private void push_xml_comment ()
+		{
+#if DEBUG
+			if (xmlCommentSavePoint > 0)
+				Console.Error.WriteLine ("WARNING!! Comments are not consumed for previous target. Current comment is " + xml_comment_buffer);
+#endif
+			xmlCommentSavePoint = xml_comment_buffer.Length;
 		}
 
 		public void cleanup ()
Index: decl.cs
===================================================================
RCS file: /cvs/public/mcs/mcs/decl.cs,v
retrieving revision 1.115
diff -u -r1.115 decl.cs
--- decl.cs	23 May 2004 22:48:34 -0000	1.115
+++ decl.cs	24 May 2004 12:42:26 -0000
@@ -15,6 +15,7 @@
 using System.Globalization;
 using System.Reflection.Emit;
 using System.Reflection;
+using System.Xml;
 
 namespace Mono.CSharp {
 
@@ -38,6 +39,11 @@
 		/// </summary>
 		public readonly Location Location;
 
+		/// <summary>
+		///   XML documentation comment
+		/// </summary>
+		public XmlElement Documentation;
+
 		[Flags]
 		public enum Flags {
 			Obsolete_Undetected = 1,		// Obsolete attribute has not been detected yet
@@ -293,7 +299,6 @@
 
 			return true;
 		}
-
 	}
 
 	/// <summary>
@@ -1083,6 +1088,12 @@
 			}
 
 			return true;
+		}
+		
+		public virtual void FixupDocument ()
+		{
+			if (Documentation != null)
+				Documentation = null; // release the reference
 		}
 
 		protected override string[] ValidAttributeTargets {
Index: driver.cs
===================================================================
RCS file: /cvs/public/mcs/mcs/driver.cs,v
retrieving revision 1.192
diff -u -r1.192 driver.cs
--- driver.cs	14 May 2004 14:32:19 -0000	1.192
+++ driver.cs	24 May 2004 12:42:26 -0000
@@ -17,6 +17,7 @@
 	using System.IO;
 	using System.Text;
 	using System.Globalization;
+	using System.Xml;
 	using System.Diagnostics;
 	using Mono.Languages;
 
@@ -102,6 +103,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;
@@ -229,6 +235,8 @@
 				"   -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" +
+				"   --parse            Only parses the source 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" + 
@@ -733,6 +741,16 @@
 				SetOutputFile (args [++i]);
 				return true;
 				
+			case "--document":
+				int docNameStart = args [i].IndexOf (':') + 1;
+				if (docNameStart == 0){
+					Report.Error (2006, "Missing ':<filename>' to --document");
+					Environment.Exit (1);
+				}
+				xml_documentation_file = args [i].Substring (docNameStart);
+				RootContext.NeedDocument = true;
+				return true;
+				
 			case "--checked":
 				RootContext.Checked = true;
 				return true;
@@ -1106,10 +1124,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": {
@@ -1525,6 +1544,22 @@
 			if (timestamps)
 				ShowTime ("Resolving tree");
 			RootContext.ResolveTree ();
+			if (RootContext.NeedDocument) {
+				RootContext.FixupDocument ();
+
+				XmlDocument doc = RootContext.XmlDocumentation;
+				XmlElement el = doc.CreateElement ("assembly");
+				string asmName = Path.ChangeExtension (output_file, null);
+				el.InnerXml = "<name>" + asmName + "</name>";
+				doc.DocumentElement.InsertAfter (el, null);
+
+				XmlTextWriter w = new XmlTextWriter (xml_documentation_file, encoding);
+				w.Formatting = Formatting.Indented;
+				doc.Save (w);
+				w.Close ();
+				RootContext.XmlDocumentation = null;
+			}
+
 			if (Report.Errors > 0)
 				return false;
 			if (timestamps)
@@ -1708,7 +1743,6 @@
 #endif
 			return (Report.Errors == 0);
 		}
-
 	}
 
 	//
Index: rootcontext.cs
===================================================================
RCS file: /cvs/public/mcs/mcs/rootcontext.cs,v
retrieving revision 1.131
diff -u -r1.131 rootcontext.cs
--- rootcontext.cs	12 May 2004 06:36:15 -0000	1.131
+++ rootcontext.cs	24 May 2004 12:42:26 -0000
@@ -13,6 +13,7 @@
 using System.Reflection;
 using System.Reflection.Emit;
 using System.Diagnostics;
+using System.Xml;
 
 namespace Mono.CSharp {
 
@@ -77,6 +78,10 @@
 		public static string StrongNameKeyContainer;
 		public static bool StrongNameDelaySign = false;
 
+		// If set, enable XML documentation generation
+		public static bool NeedDocument = false;
+		public static XmlDocument XmlDocumentation;
+
 		//
 		// Constructor
 		//
@@ -176,6 +181,28 @@
 			if (root.Enums != null)
 				foreach (Enum e in root.Enums)
 					e.DefineType ();
+		}
+
+		static public void FixupDocument ()
+		{
+			TypeContainer root = Tree.Types;
+
+			ArrayList ifaces = root.Interfaces;
+			if (ifaces != null){
+				foreach (Interface i in ifaces) 
+					i.FixupDocument ();
+			}
+
+			foreach (TypeContainer tc in root.Types)
+				tc.FixupDocument ();
+
+			if (root.Delegates != null)
+				foreach (Delegate d in root.Delegates) 
+					d.FixupDocument ();
+
+			if (root.Enums != null)
+				foreach (Enum e in root.Enums)
+					e.FixupDocument ();
 		}
 
 		static void Error_TypeConflict (string name, Location loc)

--------------090505040603060507090700--