/// <summary> /// Checks to make sure that the slashes in in the Xml header are followed by a space. /// </summary> /// <param name="header">The Xml header token.</param> private void CheckXmlHeaderComment(ElementHeader header) { Param.AssertNotNull(header, "header"); for (ElementHeaderLine item = header.FindFirstDescendent<ElementHeaderLine>(); item != null; item = item.FindNextDescendentOf<ElementHeaderLine>(header)) { if (item.Text.Length > 3) { if (item.Text[3] != ' ' && item.Text[3] != '\t' && item.Text[3] != '/' && item.Text[2] != '\n' && item.Text[2] != '\r') { // The header line does not start with any spaces. this.AddViolation(item.FindParentElement(), item.LineNumber, Rules.DocumentationLinesMustBeginWithSingleSpace); } else if (item.Text.Length > 4 && (item.Text[4] == ' ' || item.Text[4] == '\t')) { // The header line starts with more than one space. This is only allowed when the // header line is not the first or last line in the header. bool error = true; for (LexicalElement previous = item.FindPreviousLexicalElement(); previous != null; previous = previous.FindPreviousLexicalElement()) { if (previous.Is(CommentType.ElementHeaderLine)) { for (LexicalElement next = item.FindNextLexicalElement(); next != null; next = next.FindNextLexicalElement()) { if (next.Is(CommentType.ElementHeaderLine)) { error = false; break; } } break; } } if (error) { this.AddViolation(item.FindParentElement(), item.LineNumber, Rules.DocumentationLinesMustBeginWithSingleSpace); } } } } }
/// <summary> /// Loads the header text for the element into a document and returns it. /// </summary> /// <param name="element">The element containing the header.</param> /// <param name="header">The header to load.</param> /// <param name="lineNumber">The line number that the header begins on.</param> /// <param name="rawDocs">Returns the docs with whitepace and newlines left in place.</param> /// <param name="formattedDocs">Returns the docs with newlines filtered out.</param> private void LoadHeaderIntoDocuments(Element element, ElementHeader header, int lineNumber, out XmlDocument rawDocs, out XmlDocument formattedDocs) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(header, "header"); Param.AssertGreaterThanZero(lineNumber, "lineNumber"); rawDocs = new XmlDocument(); formattedDocs = new XmlDocument(); try { string correctxml = "<root>" + header.HeaderXml + "</root>"; formattedDocs.LoadXml(correctxml); correctxml = "<root>" + header.HeaderXmlWithNewlines + "</root>"; rawDocs.LoadXml(correctxml); } catch (XmlException xmlex) { this.AddViolation(element, lineNumber, Rules.DocumentationMustContainValidXml, xmlex.Message); rawDocs = formattedDocs = null; } }
/// <summary> /// Checks the documentation header to see whether it contains any blank lines. /// </summary> /// <param name="element">The element containing the header.</param> /// <param name="header">The documentation header.</param> private void CheckForBlankLinesInDocumentationHeader(Element element, ElementHeader header) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(header, "header"); if (!element.Generated) { int blankLineCount = 0; int insideCodeElementCount = 0; for (CodeUnit item = header.FindFirstChild(); item != null; item = item.FindNextSibling()) { if (item.Is(LexicalElementType.EndOfLine)) { ++blankLineCount; if (blankLineCount > 1) { this.AddViolation(element, item.LineNumber, Rules.DocumentationHeadersMustNotContainBlankLines); break; } } else if (item.Is(CommentType.ElementHeaderLine)) { ElementHeaderLine headerLine = (ElementHeaderLine)item; insideCodeElementCount += XmlHeaderLineCodeElementCount(headerLine); if (headerLine == header.Children.First || headerLine == header.Children.Last) { if (IsXmlHeaderLineEmpty(headerLine) && insideCodeElementCount == 0) { this.AddViolation(element, headerLine.LineNumber, Rules.DocumentationHeadersMustNotContainBlankLines); break; } } else if (!IsXmlHeaderLineEmpty(headerLine) || insideCodeElementCount > 0) { blankLineCount = 0; } } } } }
/// <summary> /// Parses the contents of the header for validity. /// </summary> /// <param name="element">The element.</param> /// <param name="header">The header.</param> /// <param name="lineNumber">The line number that the header element begins on.</param> /// <param name="partialElement">Indicates whether the element has the partial attribute.</param> private void ParseHeader(Element element, ElementHeader header, int lineNumber, bool partialElement) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(header, "header"); Param.AssertGreaterThanZero(lineNumber, "lineNumber"); Param.Ignore(partialElement); // Load this into an xml document. XmlDocument rawDocs = null; XmlDocument formattedDocs = null; this.LoadHeaderIntoDocuments(element, header, lineNumber, out rawDocs, out formattedDocs); if (rawDocs != null && formattedDocs != null) { // Check whether the method has an <inheritdoc> tag at the root. If so, discontinue checking the contents of the header, // but verify that the class actually inherits from a base class. Otherwise this tag is not allowed. if (rawDocs.SelectSingleNode("root/inheritdoc") != null) { this.CheckInheritDocRules(element); } else { // Insert any documentation present in 'include' tags. if (this.InsertIncludedDocumentation(element, Path.GetDirectoryName(element.Document.Path), formattedDocs)) { this.CheckForBlankLinesInDocumentationHeader(element, header); this.CheckHeaderSummary(element, lineNumber, partialElement, formattedDocs); // Check element parameters and return types. if (element.ElementType == ElementType.Method) { Method item = element as Method; this.CheckHeaderParams(element, item.ParameterList, formattedDocs); this.CheckHeaderReturnValue(element, item.ReturnType, formattedDocs); } else if (element.ElementType == ElementType.Constructor) { Constructor item = element as Constructor; this.CheckHeaderParams(element, item.ParameterList, formattedDocs); this.CheckConstructorSummaryText(item, formattedDocs); } else if (element.ElementType == ElementType.Delegate) { StyleCop.CSharp.CodeModel.Delegate item = element as StyleCop.CSharp.CodeModel.Delegate; this.CheckHeaderParams(element, item.ParameterList, formattedDocs); this.CheckHeaderReturnValue(element, item.ReturnType, formattedDocs); } else if (element.ElementType == ElementType.Indexer) { Indexer item = element as Indexer; this.CheckHeaderParams(element, item.ParameterList, formattedDocs); } else if (element.ElementType == ElementType.Property) { // Check value tags on properties. this.CheckPropertyValueTag(element, formattedDocs); // Check that the property summary starts with the correct text. this.CheckPropertySummaryFormatting(element as Property, formattedDocs); } else if (element.ElementType == ElementType.Destructor) { this.CheckDestructorSummaryText((Destructor)element, formattedDocs); } // Check for repeating comments on all element types which can contain params or typeparams. if (element.ElementType == ElementType.Method || element.ElementType == ElementType.Constructor || element.ElementType == ElementType.Delegate || element.ElementType == ElementType.Indexer || element.ElementType == ElementType.Class || element.ElementType == ElementType.Struct || element.ElementType == ElementType.Interface) { this.CheckForRepeatingComments(element, formattedDocs); } // Check generic type parameters. if (element.ElementType == ElementType.Class || element.ElementType == ElementType.Method || element.ElementType == ElementType.Delegate || element.ElementType == ElementType.Interface || element.ElementType == ElementType.Struct) { this.CheckGenericTypeParams(element, formattedDocs); } } } } }