Merge pull request #5636 from BrzVlad/fix-xmm-scan
[mono.git] / mcs / mcs / doc.cs
1 //
2 // doc.cs: Support for XML documentation comment.
3 //
4 // Authors:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //  Marek Safar (marek.safar@gmail.com>
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 //
10 // Copyright 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc
12 //
13 //
14
15 using System;
16 using System.Collections.Generic;
17 using System.IO;
18 using System.Text;
19 using System.Xml;
20 using System.Linq;
21
22 namespace Mono.CSharp
23 {
24         //
25         // Implements XML documentation generation.
26         //
27         class DocumentationBuilder
28         {
29                 //
30                 // Used to create element which helps well-formedness checking.
31                 //
32                 readonly XmlDocument XmlDocumentation;
33
34                 readonly ModuleContainer module;
35                 readonly ModuleContainer doc_module;
36
37                 //
38                 // The output for XML documentation.
39                 //
40                 XmlWriter XmlCommentOutput;
41
42                 static readonly string line_head = Environment.NewLine + "            ";
43
44                 //
45                 // Stores XmlDocuments that are included in XML documentation.
46                 // Keys are included filenames, values are XmlDocuments.
47                 //
48                 Dictionary<string, XmlDocument> StoredDocuments = new Dictionary<string, XmlDocument> ();
49
50                 ParserSession session;
51
52                 public DocumentationBuilder (ModuleContainer module)
53                 {
54                         doc_module = new ModuleContainer (module.Compiler);
55                         doc_module.DocumentationBuilder = this;
56
57                         this.module = module;
58                         XmlDocumentation = new XmlDocument ();
59                         XmlDocumentation.PreserveWhitespace = false;
60                 }
61
62                 Report Report {
63                         get {
64                                 return module.Compiler.Report;
65                         }
66                 }
67
68                 public MemberName ParsedName {
69                         get; set;
70                 }
71
72                 public List<DocumentationParameter> ParsedParameters {
73                         get; set;
74                 }
75
76                 public TypeExpression ParsedBuiltinType {
77                         get; set;
78                 }
79
80                 public Operator.OpType? ParsedOperator {
81                         get; set;
82                 }
83
84                 XmlNode GetDocCommentNode (MemberCore mc, string name)
85                 {
86                         // FIXME: It could be even optimizable as not
87                         // to use XmlDocument. But anyways the nodes
88                         // are not kept in memory.
89                         XmlDocument doc = XmlDocumentation;
90                         try {
91                                 XmlElement el = doc.CreateElement ("member");
92                                 el.SetAttribute ("name", name);
93                                 string normalized = mc.DocComment;
94                                 el.InnerXml = normalized;
95
96                                 string [] split = normalized.Split ('\n');
97                                 el.InnerXml = line_head + String.Join (line_head, split);
98                                 return el;
99                         } catch (Exception ex) {
100                                 Report.Warning (1570, 1, mc.Location, "XML documentation comment on `{0}' is not well-formed XML markup ({1})",
101                                         mc.GetSignatureForError (), ex.Message);
102
103                                 return doc.CreateComment (String.Format ("FIXME: Invalid documentation markup was found for member {0}", name));
104                         }
105                 }
106
107                 //
108                 // Generates xml doc comments (if any), and if required,
109                 // handle warning report.
110                 //
111                 internal void GenerateDocumentationForMember (MemberCore mc)
112                 {
113                         string name = mc.DocCommentHeader + mc.GetSignatureForDocumentation ();
114
115                         XmlNode n = GetDocCommentNode (mc, name);
116
117                         XmlElement el = n as XmlElement;
118                         if (el != null) {
119                                 var pm = mc as IParametersMember;
120                                 if (pm != null) {
121                                         CheckParametersComments (mc, pm, el);
122                                 }
123
124                                 // FIXME: it could be done with XmlReader
125                                 XmlNodeList nl = n.SelectNodes (".//include");
126                                 if (nl.Count > 0) {
127                                         // It could result in current node removal, so prepare another list to iterate.
128                                         var al = new List<XmlNode> (nl.Count);
129                                         foreach (XmlNode inc in nl)
130                                                 al.Add (inc);
131                                         foreach (XmlElement inc in al)
132                                                 if (!HandleInclude (mc, inc))
133                                                         inc.ParentNode.RemoveChild (inc);
134                                 }
135
136                                 // FIXME: it could be done with XmlReader
137
138                                 foreach (XmlElement see in n.SelectNodes (".//see"))
139                                         HandleSee (mc, see);
140                                 foreach (XmlElement seealso in n.SelectNodes (".//seealso"))
141                                         HandleSeeAlso (mc, seealso);
142                                 foreach (XmlElement see in n.SelectNodes (".//exception"))
143                                         HandleException (mc, see);
144                                 foreach (XmlElement node in n.SelectNodes (".//typeparam"))
145                                         HandleTypeParam (mc, node);
146                                 foreach (XmlElement node in n.SelectNodes (".//typeparamref"))
147                                         HandleTypeParamRef (mc, node);
148                         }
149
150                         n.WriteTo (XmlCommentOutput);
151                 }
152
153                 //
154                 // Processes "include" element. Check included file and
155                 // embed the document content inside this documentation node.
156                 //
157                 bool HandleInclude (MemberCore mc, XmlElement el)
158                 {
159                         bool keep_include_node = false;
160                         string file = el.GetAttribute ("file");
161                         string path = el.GetAttribute ("path");
162
163                         if (file == "") {
164                                 Report.Warning (1590, 1, mc.Location, "Invalid XML `include' element. Missing `file' attribute");
165                                 el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Include tag is invalid "), el);
166                                 keep_include_node = true;
167                         } else if (path.Length == 0) {
168                                 Report.Warning (1590, 1, mc.Location, "Invalid XML `include' element. Missing `path' attribute");
169                                 el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Include tag is invalid "), el);
170                                 keep_include_node = true;
171                         } else {
172                                 XmlDocument doc;
173                                 Exception exception = null;
174                                 var full_path = Path.Combine (Path.GetDirectoryName (mc.Location.NameFullPath), file);
175
176                                 if (!StoredDocuments.TryGetValue (full_path, out doc)) {
177                                         try {
178                                                 doc = new XmlDocument ();
179                                                 doc.Load (full_path);
180                                                 StoredDocuments.Add (full_path, doc);
181                                         } catch (Exception e) {
182                                                 exception = e;
183                                                 el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (String.Format (" Badly formed XML in at comment file `{0}': cannot be included ", file)), el);
184                                         }
185                                 }
186
187                                 if (doc != null) {
188                                         try {
189                                                 XmlNodeList nl = doc.SelectNodes (path);
190                                                 if (nl.Count == 0) {
191                                                         el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" No matching elements were found for the include tag embedded here. "), el);
192                                         
193                                                         keep_include_node = true;
194                                                 }
195                                                 foreach (XmlNode n in nl)
196                                                         el.ParentNode.InsertBefore (el.OwnerDocument.ImportNode (n, true), el);
197                                         } catch (Exception ex) {
198                                                 exception = ex;
199                                                 el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Failed to insert some or all of included XML "), el);
200                                         }
201                                 }
202
203                                 if (exception != null) {
204                                         Report.Warning (1589, 1, mc.Location, "Unable to include XML fragment `{0}' of file `{1}'. {2}",
205                                                 path, file, exception.Message);
206                                 }
207                         }
208
209                         return keep_include_node;
210                 }
211
212                 //
213                 // Handles <see> elements.
214                 //
215                 void HandleSee (MemberCore mc, XmlElement see)
216                 {
217                         HandleXrefCommon (mc, see);
218                 }
219
220                 //
221                 // Handles <seealso> elements.
222                 //
223                 void HandleSeeAlso (MemberCore mc, XmlElement seealso)
224                 {
225                         HandleXrefCommon (mc, seealso);
226                 }
227
228                 //
229                 // Handles <exception> elements.
230                 //
231                 void HandleException (MemberCore mc, XmlElement seealso)
232                 {
233                         HandleXrefCommon (mc, seealso);
234                 }
235
236                 //
237                 // Handles <typeparam /> node
238                 //
239                 static void HandleTypeParam (MemberCore mc, XmlElement node)
240                 {
241                         if (!node.HasAttribute ("name"))
242                                 return;
243
244                         string tp_name = node.GetAttribute ("name");
245                         if (mc.CurrentTypeParameters != null) {
246                                 if (mc.CurrentTypeParameters.Find (tp_name) != null)
247                                         return;
248                         }
249                         
250                         // TODO: CS1710, CS1712
251                         
252                         mc.Compiler.Report.Warning (1711, 2, mc.Location,
253                                 "XML comment on `{0}' has a typeparam name `{1}' but there is no type parameter by that name",
254                                 mc.GetSignatureForError (), tp_name);
255                 }
256
257                 //
258                 // Handles <typeparamref /> node
259                 //
260                 static void HandleTypeParamRef (MemberCore mc, XmlElement node)
261                 {
262                         if (!node.HasAttribute ("name"))
263                                 return;
264
265                         string tp_name = node.GetAttribute ("name");
266                         var member = mc;
267                         do {
268                                 if (member.CurrentTypeParameters != null) {
269                                         if (member.CurrentTypeParameters.Find (tp_name) != null)
270                                                 return;
271                                 }
272
273                                 member = member.Parent;
274                         } while (member != null);
275
276                         mc.Compiler.Report.Warning (1735, 2, mc.Location,
277                                 "XML comment on `{0}' has a typeparamref name `{1}' that could not be resolved",
278                                 mc.GetSignatureForError (), tp_name);
279                 }
280
281                 FullNamedExpression ResolveMemberName (IMemberContext context, MemberName mn)
282                 {
283                         if (mn.Left == null)
284                                 return context.LookupNamespaceOrType (mn.Name, mn.Arity, LookupMode.Probing, Location.Null);
285
286                         var left = ResolveMemberName (context, mn.Left);
287                         var ns = left as NamespaceExpression;
288                         if (ns != null)
289                                 return ns.LookupTypeOrNamespace (context, mn.Name, mn.Arity, LookupMode.Probing, Location.Null);
290
291                         TypeExpr texpr = left as TypeExpr;
292                         if (texpr != null) {
293                                 var found = MemberCache.FindNestedType (texpr.Type, mn.Name, mn.Arity, false);
294                                 if (found != null)
295                                         return new TypeExpression (found, Location.Null);
296
297                                 return null;
298                         }
299
300                         return left;
301                 }
302
303                 //
304                 // Processes "see" or "seealso" elements from cref attribute.
305                 //
306                 void HandleXrefCommon (MemberCore mc, XmlElement xref)
307                 {
308                         string cref = xref.GetAttribute ("cref");
309                         // when, XmlReader, "if (cref == null)"
310                         if (!xref.HasAttribute ("cref"))
311                                 return;
312
313                         // Nothing to be resolved the reference is marked explicitly
314                         if (cref.Length > 2 && cref [1] == ':')
315                                 return;
316
317                         // Additional symbols for < and > are allowed for easier XML typing
318                         cref = cref.Replace ('{', '<').Replace ('}', '>');
319
320                         var encoding = module.Compiler.Settings.Encoding;
321                         var s = new MemoryStream (encoding.GetBytes (cref));
322
323                         var source_file = new CompilationSourceFile (doc_module, mc.Location.SourceFile);
324                         var report = new Report (doc_module.Compiler, new NullReportPrinter ());
325
326                         if (session == null)
327                                 session = new ParserSession {
328                                         UseJayGlobalArrays = true
329                                 };
330
331                         SeekableStreamReader seekable = new SeekableStreamReader (s, encoding, session.StreamReaderBuffer);
332
333                         var parser = new CSharpParser (seekable, source_file, report, session);
334                         ParsedParameters = null;
335                         ParsedName = null;
336                         ParsedBuiltinType = null;
337                         ParsedOperator = null;
338                         parser.Lexer.putback_char = Tokenizer.DocumentationXref;
339                         parser.Lexer.parsing_generic_declaration_doc = true;
340                         parser.parse ();
341                         if (report.Errors > 0) {
342                                 Report.Warning (1584, 1, mc.Location, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'",
343                                         mc.GetSignatureForError (), cref);
344
345                                 xref.SetAttribute ("cref", "!:" + cref);
346                                 return;
347                         }
348
349                         MemberSpec member;
350                         string prefix = null;
351                         FullNamedExpression fne = null;
352
353                         //
354                         // Try built-in type first because we are using ParsedName as identifier of
355                         // member names on built-in types
356                         //
357                         if (ParsedBuiltinType != null && (ParsedParameters == null || ParsedName != null)) {
358                                 member = ParsedBuiltinType.Type;
359                         } else {
360                                 member = null;
361                         }
362
363                         if (ParsedName != null || ParsedOperator.HasValue) {
364                                 TypeSpec type = null;
365                                 string member_name = null;
366
367                                 if (member == null) {
368                                         if (ParsedOperator.HasValue) {
369                                                 type = mc.CurrentType;
370                                         } else if (ParsedName.Left != null) {
371                                                 fne = ResolveMemberName (mc, ParsedName.Left);
372                                                 if (fne != null) {
373                                                         var ns = fne as NamespaceExpression;
374                                                         if (ns != null) {
375                                                                 fne = ns.LookupTypeOrNamespace (mc, ParsedName.Name, ParsedName.Arity, LookupMode.Probing, Location.Null);
376                                                                 if (fne != null) {
377                                                                         member = fne.Type;
378                                                                 }
379                                                         } else {
380                                                                 type = fne.Type;
381                                                         }
382                                                 }
383                                         } else {
384                                                 fne = ResolveMemberName (mc, ParsedName);
385                                                 if (fne == null) {
386                                                         type = mc.CurrentType;
387                                                 } else if (ParsedParameters == null) {
388                                                         member = fne.Type;
389                                                 } else if (fne.Type.MemberDefinition == mc.CurrentType.MemberDefinition) {
390                                                         member_name = Constructor.ConstructorName;
391                                                         type = fne.Type;
392                                                 }
393                                         }
394                                 } else {
395                                         type = (TypeSpec) member;
396                                         member = null;
397                                 }
398
399                                 if (ParsedParameters != null) {
400                                         var old_printer = mc.Module.Compiler.Report.SetPrinter (new NullReportPrinter ());
401                                         try {
402                                                 var context = new DocumentationMemberContext (mc, ParsedName ?? MemberName.Null);
403
404                                                 foreach (var pp in ParsedParameters) {
405                                                         pp.Resolve (context);
406                                                 }
407                                         } finally {
408                                                 mc.Module.Compiler.Report.SetPrinter (old_printer);
409                                         }
410                                 }
411
412                                 if (type != null) {
413                                         if (member_name == null)
414                                                 member_name = ParsedOperator.HasValue ?
415                                                         Operator.GetMetadataName (ParsedOperator.Value) : ParsedName.Name;
416
417                                         int parsed_param_count;
418                                         if (ParsedOperator == Operator.OpType.Explicit || ParsedOperator == Operator.OpType.Implicit) {
419                                                 parsed_param_count = ParsedParameters.Count - 1;
420                                         } else if (ParsedParameters != null) {
421                                                 parsed_param_count = ParsedParameters.Count;
422                                         } else {
423                                                 parsed_param_count = 0;
424                                         }
425
426                                         int parameters_match = -1;
427                                         do {
428                                                 var members = MemberCache.FindMembers (type, member_name, true);
429                                                 if (members != null) {
430                                                         foreach (var m in members) {
431                                                                 if (ParsedName != null && m.Arity != ParsedName.Arity)
432                                                                         continue;
433
434                                                                 if (ParsedParameters != null) {
435                                                                         IParametersMember pm = m as IParametersMember;
436                                                                         if (pm == null)
437                                                                                 continue;
438
439                                                                         if (m.Kind == MemberKind.Operator && !ParsedOperator.HasValue)
440                                                                                 continue;
441
442                                                                         var pm_params = pm.Parameters;
443
444                                                                         int i;
445                                                                         for (i = 0; i < parsed_param_count; ++i) {
446                                                                                 var pparam = ParsedParameters[i];
447
448                                                                                 if (i >= pm_params.Count || pparam == null || pparam.TypeSpec == null ||
449                                                                                         !TypeSpecComparer.Override.IsEqual (pparam.TypeSpec, pm_params.Types[i]) ||
450                                                                                         (pparam.Modifier & Parameter.Modifier.RefOutMask) != (pm_params.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) {
451
452                                                                                         if (i > parameters_match) {
453                                                                                                 parameters_match = i;
454                                                                                         }
455
456                                                                                         i = -1;
457                                                                                         break;
458                                                                                 }
459                                                                         }
460
461                                                                         if (i < 0)
462                                                                                 continue;
463
464                                                                         if (ParsedOperator == Operator.OpType.Explicit || ParsedOperator == Operator.OpType.Implicit) {
465                                                                                 if (pm.MemberType != ParsedParameters[parsed_param_count].TypeSpec) {
466                                                                                         parameters_match = parsed_param_count + 1;
467                                                                                         continue;
468                                                                                 }
469                                                                         } else {
470                                                                                 if (parsed_param_count != pm_params.Count)
471                                                                                         continue;
472                                                                         }
473                                                                 }
474
475                                                                 if (member != null) {
476                                                                         Report.Warning (419, 3, mc.Location,
477                                                                                 "Ambiguous reference in cref attribute `{0}'. Assuming `{1}' but other overloads including `{2}' have also matched",
478                                                                                 cref, member.GetSignatureForError (), m.GetSignatureForError ());
479
480                                                                         break;
481                                                                 }
482
483                                                                 member = m;
484                                                         }
485                                                 }
486
487                                                 // Continue with parent type for nested types
488                                                 if (member == null) {
489                                                         type = type.DeclaringType;
490                                                 } else {
491                                                         type = null;
492                                                 }
493                                         } while (type != null);
494
495                                         if (member == null && parameters_match >= 0) {
496                                                 for (int i = parameters_match; i < parsed_param_count; ++i) {
497                                                         Report.Warning (1580, 1, mc.Location, "Invalid type for parameter `{0}' in XML comment cref attribute `{1}'",
498                                                                         (i + 1).ToString (), cref);
499                                                 }
500
501                                                 if (parameters_match == parsed_param_count + 1) {
502                                                         Report.Warning (1581, 1, mc.Location, "Invalid return type in XML comment cref attribute `{0}'", cref);
503                                                 }
504                                         }
505                                 }
506                         }
507
508                         if (member == null) {
509                                 Report.Warning (1574, 1, mc.Location, "XML comment on `{0}' has cref attribute `{1}' that could not be resolved",
510                                         mc.GetSignatureForError (), cref);
511                                 cref = "!:" + cref;
512                         } else if (member == InternalType.Namespace) {
513                                 cref = "N:" + fne.GetSignatureForError ();
514                         } else {
515                                 prefix = GetMemberDocHead (member);
516                                 cref = prefix + member.GetSignatureForDocumentation ();
517                         }
518
519                         xref.SetAttribute ("cref", cref);
520                 }
521
522                 //
523                 // Get a prefix from member type for XML documentation (used
524                 // to formalize cref target name).
525                 //
526                 static string GetMemberDocHead (MemberSpec type)
527                 {
528                         if (type is FieldSpec)
529                                 return "F:";
530                         if (type is MethodSpec)
531                                 return "M:";
532                         if (type is EventSpec)
533                                 return "E:";
534                         if (type is PropertySpec)
535                                 return "P:";
536                         if (type is TypeSpec)
537                                 return "T:";
538
539                         throw new NotImplementedException (type.GetType ().ToString ());
540                 }
541
542                 //
543                 // Raised (and passed an XmlElement that contains the comment)
544                 // when GenerateDocComment is writing documentation expectedly.
545                 //
546                 // FIXME: with a few effort, it could be done with XmlReader,
547                 // that means removal of DOM use.
548                 //
549                 void CheckParametersComments (MemberCore member, IParametersMember paramMember, XmlElement el)
550                 {
551                         HashSet<string> found_tags = null;
552                         foreach (XmlElement pelem in el.SelectNodes ("param")) {
553                                 string xname = pelem.GetAttribute ("name");
554                                 if (xname.Length == 0)
555                                         continue; // really? but MS looks doing so
556
557                                 if (found_tags == null) {
558                                         found_tags = new HashSet<string> ();
559                                 }
560
561                                 if (xname != "" && paramMember.Parameters.GetParameterIndexByName (xname) < 0) {
562                                         Report.Warning (1572, 2, member.Location,
563                                                 "XML comment on `{0}' has a param tag for `{1}', but there is no parameter by that name",
564                                                 member.GetSignatureForError (), xname);
565                                         continue;
566                                 }
567
568                                 if (found_tags.Contains (xname)) {
569                                         Report.Warning (1571, 2, member.Location,
570                                                 "XML comment on `{0}' has a duplicate param tag for `{1}'",
571                                                 member.GetSignatureForError (), xname);
572                                         continue;
573                                 }
574
575                                 found_tags.Add (xname);
576                         }
577
578                         if (found_tags != null) {
579                                 foreach (Parameter p in paramMember.Parameters.FixedParameters) {
580                                         if (!found_tags.Contains (p.Name) && !(p is ArglistParameter))
581                                                 Report.Warning (1573, 4, member.Location,
582                                                         "Parameter `{0}' has no matching param tag in the XML comment for `{1}'",
583                                                         p.Name, member.GetSignatureForError ());
584                                 }
585                         }
586                 }
587
588                 //
589                 // Outputs XML documentation comment from tokenized comments.
590                 //
591                 public bool OutputDocComment (string asmfilename, string xmlFileName)
592                 {
593                         XmlTextWriter w = null;
594                         try {
595                                 w = new XmlTextWriter (xmlFileName, null);
596                                 w.Indentation = 4;
597                                 w.Formatting = Formatting.Indented;
598                                 w.WriteStartDocument ();
599                                 w.WriteStartElement ("doc");
600                                 w.WriteStartElement ("assembly");
601                                 w.WriteStartElement ("name");
602                                 w.WriteString (Path.GetFileNameWithoutExtension (asmfilename));
603                                 w.WriteEndElement (); // name
604                                 w.WriteEndElement (); // assembly
605                                 w.WriteStartElement ("members");
606                                 XmlCommentOutput = w;
607                                 module.GenerateDocComment (this);
608                                 w.WriteFullEndElement (); // members
609                                 w.WriteEndElement ();
610                                 w.WriteWhitespace (Environment.NewLine);
611                                 w.WriteEndDocument ();
612                                 return true;
613                         } catch (Exception ex) {
614                                 Report.Error (1569, "Error generating XML documentation file `{0}' (`{1}')", xmlFileName, ex.Message);
615                                 return false;
616                         } finally {
617                                 if (w != null)
618                                         w.Close ();
619                         }
620                 }
621         }
622
623         //
624         // Type lookup of documentation references uses context of type where
625         // the reference is used but type parameters from cref value
626         //
627         sealed class DocumentationMemberContext : IMemberContext
628         {
629                 readonly MemberCore host;
630                 MemberName contextName;
631
632                 public DocumentationMemberContext (MemberCore host, MemberName contextName)
633                 {
634                         this.host = host;
635                         this.contextName = contextName;
636                 }
637
638                 public TypeSpec CurrentType {
639                         get {
640                                 return host.CurrentType;
641                         }
642                 }
643
644                 public TypeParameters CurrentTypeParameters {
645                         get {
646                                 return contextName.TypeParameters;
647                         }
648                 }
649
650                 public MemberCore CurrentMemberDefinition {
651                         get {
652                                 return host.CurrentMemberDefinition;
653                         }
654                 }
655
656                 public bool IsObsolete {
657                         get {
658                                 return false;
659                         }
660                 }
661
662                 public bool IsUnsafe {
663                         get {
664                                 return host.IsStatic;
665                         }
666                 }
667
668                 public bool IsStatic {
669                         get {
670                                 return host.IsStatic;
671                         }
672                 }
673
674                 public ModuleContainer Module {
675                         get {
676                                 return host.Module;
677                         }
678                 }
679
680                 public string GetSignatureForError ()
681                 {
682                         return host.GetSignatureForError ();
683                 }
684
685                 public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity)
686                 {
687                         return null;
688                 }
689
690                 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
691                 {
692                         if (arity == 0) {
693                                 var tp = CurrentTypeParameters;
694                                 if (tp != null) {
695                                         for (int i = 0; i < tp.Count; ++i) {
696                                                 var t = tp[i];
697                                                 if (t.Name == name) {
698                                                         t.Type.DeclaredPosition = i;
699                                                         return new TypeParameterExpr (t, loc);
700                                                 }
701                                         }
702                                 }
703                         }
704
705                         return host.Parent.LookupNamespaceOrType (name, arity, mode, loc);
706                 }
707
708                 public FullNamedExpression LookupNamespaceAlias (string name)
709                 {
710                         throw new NotImplementedException ();
711                 }
712         }
713
714         class DocumentationParameter
715         {
716                 public readonly Parameter.Modifier Modifier;
717                 public FullNamedExpression Type;
718                 TypeSpec type;
719
720                 public DocumentationParameter (Parameter.Modifier modifier, FullNamedExpression type)
721                         : this (type)
722                 {
723                         this.Modifier = modifier;
724                 }
725
726                 public DocumentationParameter (FullNamedExpression type)
727                 {
728                         this.Type = type;
729                 }
730
731                 public TypeSpec TypeSpec {
732                         get {
733                                 return type;
734                         }
735                 }
736
737                 public void Resolve (IMemberContext context)
738                 {
739                         type = Type.ResolveAsType (context);
740                 }
741         }
742 }