/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs

http://github.com/icsharpcode/ILSpy · C# · 755 lines · 555 code · 109 blank · 91 comment · 171 complexity · ba2cdcc65c692c4e915f0cb3a8f67784 MD5 · raw file

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