/// <summary> /// Creates a text string based on the child tokens in the token. /// </summary> protected override void CreateTextString() { var text = new StringBuilder(); for (LexicalElement lex = this.FindFirstDescendentLexicalElement(); lex != null; lex = lex.FindNextDescendentLexicalElementOf(this)) { if (lex.Children.LexicalElementCount == 0) { text.Append(lex.Text); } } this.Text = text.ToString(); }
/// <summary> /// Calculates the location of the given element, and advances the starting location past this element for the next item. /// </summary> /// <param name="lex">The element.</param> /// <param name="lineNumber">The starting line number.</param> /// <param name="indexOnLine">The starting index of the item on the line.</param> /// <param name="indexInDocument">The start index of the item within the document.</param> /// <returns>Returns the location of the item.</returns> private static CodeLocation CalculateLocation(LexicalElement lex, ref int lineNumber, ref int indexOnLine, ref int indexInDocument) { Param.AssertNotNull(lex, "lex"); Param.AssertGreaterThanZero(lineNumber, "lineNumber"); Param.AssertGreaterThanOrEqualToZero(indexOnLine, "indexOnLine"); Param.AssertGreaterThanOrEqualToZero(indexInDocument, "indexInDocument"); string text = lex.Text; if (string.IsNullOrEmpty(text)) { return(new CodeLocation(indexInDocument, indexInDocument, indexOnLine, indexOnLine, lineNumber, lineNumber)); } int endLineNumber = lineNumber; int endIndexOnLine = indexOnLine; int endIndexInDocument = indexInDocument; for (int i = 0; i < text.Length; ++i) { ++endIndexInDocument; if (i > 0 && text[i - 1] == '\n') { ++endLineNumber; endIndexOnLine = 0; } else { ++endIndexOnLine; } } var location = new CodeLocation(indexInDocument, endIndexInDocument, indexOnLine, endIndexOnLine, lineNumber, endLineNumber); lineNumber = endLineNumber; indexOnLine = endIndexOnLine + 1; indexInDocument = endIndexInDocument + 1; if (text[text.Length - 1] == '\n') { ++lineNumber; indexOnLine = 0; } return(location); }
/// <summary> /// Fills in the locations of child elements within a lexical element. /// </summary> /// <param name="lex">The lexical element.</param> private static void SetLexicalElementChildLocations(LexicalElement lex) { Param.AssertNotNull(lex, "lex"); int lineNumber = lex.Location.StartPoint.LineNumber; int indexOnLine = lex.Location.StartPoint.Index; int indexInDocument = lex.Location.StartPoint.Index; for (LexicalElement child = lex.FindFirstChildLexicalElement(); child != null; child = child.FindNextSiblingLexicalElement()) { child.Location = CalculateLocation(child, ref lineNumber, ref indexOnLine, ref indexInDocument); if (child.Children.Count > 0) { SetLexicalElementChildLocations(child); } } }
/// <summary> /// Determines whether the type of the given token is allowed /// to appear after a closing parenthesis, with no space between /// the parenthesis and the token. /// </summary> /// <param name="item">The item to check.</param> /// <returns>True if it is allowed; false otherwise.</returns> private static bool IsAllowedAfterClosingParenthesis(LexicalElement item) { Param.AssertNotNull(item, "item"); if (item.Is(TokenType.CloseParenthesis) || item.Is(TokenType.OpenParenthesis) || item.Is(TokenType.CloseSquareBracket) || item.Is(TokenType.OpenSquareBracket) || item.Is(TokenType.CloseAttributeBracket) || item.Is(TokenType.Semicolon) || item.Is(TokenType.Comma) || item.Is(OperatorType.Decrement) || item.Is(OperatorType.Increment) || item.Is(OperatorType.MemberAccess) || item.Is(OperatorType.Pointer)) { return true; } return false; }
/// <summary> /// Writes the contents of the document to the given writer. /// </summary> /// <param name="writer">The writer.</param> public void Write(TextWriter writer) { Param.RequireNotNull(writer, "writer"); for (LexicalElement item = this.FindFirstLexicalElement(); item != null; item = item.FindNextLexicalElement()) { if (item.LexicalElementType == LexicalElementType.PreprocessorDirective || item.Children.LexicalElementCount == 0) { if (item.LexicalElementType == LexicalElementType.EndOfLine) { writer.WriteLine(); } else { writer.Write(item.Text); } } if (item.LexicalElementType == LexicalElementType.PreprocessorDirective) { item = item.FindLastLexicalElement(); } } }
/// <summary> /// Checks for tabs in the given comment. /// </summary> /// <param name="comment">The comment token.</param> private void CheckTabsInComment(LexicalElement comment) { Param.AssertNotNull(comment, "comment"); int lineEnds = 0; for (int i = 0; i < comment.Text.Length; ++i) { if (comment.Text[i] == '\t') { this.AddViolation(comment.FindParentElement(), comment.LineNumber + lineEnds, Rules.TabsMustNotBeUsed); } else if (comment.Text[i] == '\n') { ++lineEnds; } } }
/// <summary> /// Updates the regions, generated code status, and locations of items in the document. /// </summary> internal void Update() { int lineNumber = 1; int indexOnLine = 0; int indexInDocument = 0; int generatedCount = 0; Stack <RegionDirective> regionStack = new Stack <RegionDirective>(); // If the document is marked as generated, then treat everything in the document as generated. if (this.Generated) { ++generatedCount; } for (LexicalElement lex = this.FindFirstLexicalElement(); lex != null; lex = lex.FindNextLexicalElement()) { if (!lex.Parent.Is(CodeUnitType.LexicalElement)) { lex.Location = CalculateLocation(lex, ref lineNumber, ref indexOnLine, ref indexInDocument); } if (lex.Children.LexicalElementCount > 0) { SetLexicalElementChildLocations(lex); } lex.Generated = generatedCount > 0; if (lex.Is(PreprocessorType.Region)) { RegionDirective region = (RegionDirective)lex; regionStack.Push(region); if (region.IsGeneratedCodeRegion) { ++generatedCount; } } else if (lex.Is(PreprocessorType.EndRegion)) { EndRegionDirective endRegion = (EndRegionDirective)lex; if (regionStack.Count == 0) { throw new SyntaxException(this.Document, endRegion.LineNumber, Strings.NoMatchingRegion); } RegionDirective region = regionStack.Pop(); endRegion.Partner = region; region.Partner = endRegion; if (region.IsGeneratedCodeRegion) { --generatedCount; } } } if (regionStack.Count > 0) { throw new SyntaxException(this.Document, this.LineNumber, Strings.NoMatchingEndregion); } }
/// <summary> /// Processes a newline character found while checking line spacing rules. /// </summary> /// <param name="precedingItem">The preceding item before the newline.</param> /// <param name="item">The newline item.</param> /// <param name="count">The current newline count.</param> private void CheckLineSpacingNewline(LexicalElement precedingItem, LexicalElement item, int count) { Param.Ignore(precedingItem); Param.AssertNotNull(item, "item"); Param.AssertGreaterThanOrEqualToZero(count, "count"); // If we've seen two end-of-line characters in a row, then there is a blank // line in the code. if (count == 2) { // Check whether we've seen at least one token before this blank line. if (precedingItem != null && !precedingItem.Generated) { if (precedingItem.Is(TokenType.OpenCurlyBracket)) { // The blank line comes just after an opening curly bracket. this.AddViolation( precedingItem.FindParentElement(), precedingItem.LineNumber, Rules.OpeningCurlyBracketsMustNotBeFollowedByBlankLine); } else if (IsXmlHeader(precedingItem)) { // The blank line comes just after an Xml header. this.AddViolation( precedingItem.FindParentElement(), precedingItem.LineNumber, Rules.ElementDocumentationHeadersMustNotBeFollowedByBlankLine); } } } else if (count == 3 && !item.Generated) { // There are two or more blank lines in a row. this.AddViolation( item.FindParentElement(), item.LineNumber, Rules.CodeMustNotContainMultipleBlankLinesInARow); } }
private void CheckLineSpacingNonWhitespace( CsDocument document, LexicalElement precedingItem, LexicalElement item, bool fileHeader, bool firstTokenOnLine, int count) { Param.AssertNotNull(document, "document"); Param.Ignore(precedingItem); Param.AssertNotNull(item, "item"); Param.Ignore(fileHeader); Param.Ignore(firstTokenOnLine); Param.AssertGreaterThanOrEqualToZero(count, "count"); // Skip generated tokens. if (!item.Generated) { // If there is at least one blank line in front of the current token. if (count > 1) { if (item.Is(TokenType.CloseCurlyBracket)) { // The blank line is just before a closing curly bracket. this.AddViolation( item.FindParentElement(), item.LineNumber, Rules.ClosingCurlyBracketsMustNotBePrecededByBlankLine); } else if (item.Is(TokenType.OpenCurlyBracket)) { // The blank line is just before an opening curly bracket. this.AddViolation( item.FindParentElement(), item.LineNumber, Rules.OpeningCurlyBracketsMustNotBePrecededByBlankLine); } else if (item.Is(TokenType.Else) || item.Is(TokenType.Catch) || item.Is(TokenType.Finally)) { // The blank line is just before an else, catch, or finally statement. this.AddViolation( item.FindParentElement(), item.LineNumber, Rules.ChainedStatementBlocksMustNotBePrecededByBlankLine); } else if (item.Is(TokenType.WhileDo)) { // The blank line is just before the while keyword from a do/while statement. this.AddViolation( item.FindParentElement(), item.LineNumber, Rules.WhileDoFooterMustNotBePrecededByBlankLine); } // Check if there is a blank line after a single-line comment. The exceptions // are if this is the file header, or if the line after the blank line contains // another comment or an Xml header. This is ok if the comment is not // the first item on its line. if (!fileHeader && precedingItem != null && precedingItem.Is(CommentType.SingleLineComment) && !item.Is(LexicalElementType.Comment) && !IsXmlHeader(item)) { Comment precedingComment = (Comment)precedingItem; // Now check whether the comment is the first item on its line. If the comment // is not the first item on the line, then this is not a violation. bool itemSeen = false; for (LexicalElement lineItem = precedingComment.FindPreviousLexicalElement(); lineItem != null; lineItem = lineItem.FindPreviousLexicalElement()) { if (lineItem.LexicalElementType == LexicalElementType.EndOfLine) { break; } else if (lineItem.LexicalElementType != LexicalElementType.WhiteSpace) { itemSeen = true; break; } } // Now make sure this comment does not begin with '////'. If so, this is the signal // that StyleCop should ignore this particular error. This is used when the // developer is commenting out a line of code. In this case it is not a true comment. string trimmedComment = precedingComment.Text.Trim(); if (!itemSeen && !trimmedComment.StartsWith(@"////", StringComparison.Ordinal)) { // The blank line appears after a file header, we want to allow this. if (!IsCommentInFileHeader(precedingComment)) { this.AddViolation( precedingComment.FindParentElement(), precedingComment.LineNumber, Rules.SingleLineCommentsMustNotBeFollowedByBlankLine); } } } } else if (count == 1) { // There is one line return in front of the current token, which means // the line in front of the current token is not blank. Check if the current // token is an Xml header. if (IsXmlHeader(item)) { // This is a violation unless the previous line contains another // Xml header, an opening curly bracket, or a preprocessor directive. if (precedingItem != null && !IsXmlHeader(precedingItem) && !precedingItem.Is(TokenType.OpenCurlyBracket) && !IsPreprocessorDirective(precedingItem)) { this.AddViolation( item.FindParentElement(), item.LineNumber, Rules.ElementDocumentationHeaderMustBePrecededByBlankLine); } } else if (item.Is(CommentType.SingleLineComment)) { // The current line contains a single line comment and the previous line // is not blank. This is a violation unless the previous line contains // another single line comment, an opening curly bracket, a preprocessor // directive, or if the last character on the previous line is a colon, // which can only mean that it is a label or a case statement, in which // case this is ok. if (precedingItem != null && !precedingItem.Is(CommentType.SingleLineComment) && !precedingItem.Is(TokenType.OpenCurlyBracket) && !precedingItem.Is(TokenType.LabelColon) && !IsPreprocessorDirective(precedingItem)) { // Now make sure this comment does not begin with '////'. If so, this is the signal // that StyleCop should ignore this particular error. This is used when the // developer is commenting out a line of code. In this case it is not a true comment. string trimmedComment = item.Text.Trim(); if (!trimmedComment.StartsWith(@"////", StringComparison.Ordinal)) { this.AddViolation( item.FindParentElement(), item.LineNumber, Rules.SingleLineCommentMustBePrecededByBlankLine); } } } if (precedingItem != null && precedingItem.Is(TokenType.CloseCurlyBracket)) { // Closing curly brackets cannot be followed directly by another bracket keyword. CloseCurlyBracketToken closingCurlyBracket = (CloseCurlyBracketToken)precedingItem; if (closingCurlyBracket.MatchingBracket != null && closingCurlyBracket.MatchingBracket.LineNumber != closingCurlyBracket.LineNumber && firstTokenOnLine && !item.Is(TokenType.CloseCurlyBracket) && !item.Is(TokenType.Finally) && !item.Is(TokenType.Catch) && !item.Is(TokenType.WhileDo) && !item.Is(TokenType.Else) && !IsPreprocessorDirective(item)) { this.AddViolation( closingCurlyBracket.FindParentElement(), closingCurlyBracket.LineNumber, Rules.ClosingCurlyBracketMustBeFollowedByBlankLine); } } } } }