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