Example #1
0
        /// <summary>
        /// Determines whether the given token is part of a file header. It is considered
        /// part of a file header if the only tokens in front of it are single-line comments,
        /// whitespace, or newlines.
        /// </summary>
        /// <param name="comment">The comment to check.</param>
        /// <returns>Returns true if the comment is part of a file header.</returns>
        private static bool IsCommentInFileHeader(Comment comment)
        {
            Param.AssertNotNull(comment, "comment");

            LexicalElement item = comment;

            while (item != null)
            {
                if (!item.Is(CommentType.SingleLineComment) &&
                    item.LexicalElementType != LexicalElementType.WhiteSpace &&
                    item.LexicalElementType != LexicalElementType.EndOfLine)
                {
                    return(false);
                }

                item = item.FindPreviousLexicalElement();
            }

            return(true);
        }
Example #2
0
        /// <summary>
        /// Determines whether the given bracket is the only thing on its line or whether it shares the line.
        /// </summary>
        /// <param name="bracketNode">The bracket to check.</param>
        /// <param name="allowTrailingCharacters">Indicates whether a semicolon, comma or closing parenthesis after the
        /// bracket is allowed.</param>
        /// <returns>Returns true if the bracket shares the line with something else.</returns>
        private static bool BracketSharesLine(Token bracketNode, bool allowTrailingCharacters)
        {
            Param.AssertNotNull(bracketNode, "bracketNode");
            Param.Ignore(allowTrailingCharacters);

            // Look forward.
            bool sharesLine = false;

            // Find the next non-whitespace or comment item.
            LexicalElement nextItem = null;

            for (LexicalElement item = bracketNode.FindNextLexicalElement(); item != null; item = item.FindNextLexicalElement())
            {
                if (item.LexicalElementType == LexicalElementType.EndOfLine)
                {
                    break;
                }
                else if (item.LexicalElementType != LexicalElementType.WhiteSpace &&
                         item.LexicalElementType != LexicalElementType.Comment)
                {
                    nextItem = item;
                    break;
                }
            }

            if (nextItem != null)
            {
                if (!allowTrailingCharacters ||
                    (!nextItem.Is(TokenType.Semicolon) &&
                     !nextItem.Is(TokenType.Comma) &&
                     !nextItem.Is(TokenType.CloseParenthesis) &&
                     !nextItem.Is(TokenType.CloseSquareBracket)))
                {
                    sharesLine = true;
                }
            }

            if (!sharesLine)
            {
                // Look backwards.
                for (LexicalElement item = bracketNode.FindPreviousLexicalElement(); item != null; item = item.FindPreviousLexicalElement())
                {
                    if (item.LexicalElementType == LexicalElementType.EndOfLine)
                    {
                        break;
                    }
                    else if (item.LexicalElementType != LexicalElementType.WhiteSpace && !item.Is(CommentType.SingleLineComment))
                    {
                        sharesLine = true;
                        break;
                    }
                }
            }

            return(sharesLine);
        }
Example #3
0
        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);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Checks a single-line comment to see if it is empty.
        /// </summary>
        /// <param name="comment">The comment.</param>
        /// <param name="parentElement">The parent element.</param>
        private void CheckSingleLineComment(Comment comment, Element parentElement)
        {
            Param.AssertNotNull(comment, "comment");
            Param.AssertNotNull(parentElement, "parentElement");

            // This is a single line comment.
            int  slashCount = 0;
            bool found      = false;

            // Loop through the characters in the comment text.
            for (int character = 0; character < comment.Text.Length; ++character)
            {
                // See if we've found the slashes at the beginning of the comment
                if (slashCount == 2)
                {
                    // Check whether there's anything here other than whitespace.
                    // If so, then this comment is ok.
                    if (comment.Text[character] != ' ' &&
                        comment.Text[character] != '\t' &&
                        comment.Text[character] != '\r' &&
                        comment.Text[character] != '\n')
                    {
                        found = true;
                        break;
                    }
                }
                else if (comment.Text[character] == '/')
                {
                    ++slashCount;
                }
            }

            // Check whether the comment contained any text.
            if (!found)
            {
                // This is only allowed if this comment is sandwiched in between two other comments.
                bool isComment = false;
                int  lines     = 0;
                for (LexicalElement item = comment.FindPreviousLexicalElement(); item != null; item = item.FindPreviousLexicalElement())
                {
                    if (item.Text == "\n")
                    {
                        ++lines;
                        if (lines > 1)
                        {
                            break;
                        }
                    }
                    else if (item.Is(CommentType.SingleLineComment))
                    {
                        isComment = true;
                        break;
                    }
                    else if (item.LexicalElementType != LexicalElementType.WhiteSpace)
                    {
                        break;
                    }
                }

                if (!isComment)
                {
                    this.AddViolation(parentElement, comment.LineNumber, Rules.CommentsMustContainText);
                }
                else
                {
                    isComment = false;
                    lines     = 0;
                    for (LexicalElement item = comment.FindNextLexicalElement(); item != null; item = item.FindNextLexicalElement())
                    {
                        if (item.Text == "\n")
                        {
                            ++lines;
                            if (lines > 1)
                            {
                                break;
                            }
                        }
                        else if (item.Is(CommentType.SingleLineComment))
                        {
                            isComment = true;
                            break;
                        }
                        else if (item.LexicalElementType != LexicalElementType.WhiteSpace)
                        {
                            break;
                        }
                    }

                    if (!isComment)
                    {
                        this.AddViolation(parentElement, comment.LineNumber, Rules.CommentsMustContainText);
                    }
                }
            }
        }
        /// <summary>
        /// Gets the non-whitespace item that appears before the given item.
        /// </summary>
        /// <param name="item">The original item.</param>
        /// <returns>Returns the previous item.</returns>
        private static LexicalElement GetPreviousNonWhitespaceItem(CodeUnit item)
        {
            Param.AssertNotNull(item, "item");

            for (LexicalElement previous = item.FindPreviousLexicalElement(); previous != null; previous = previous.FindPreviousLexicalElement())
            {
                if (!previous.Is(LexicalElementType.EndOfLine) && !previous.Is(LexicalElementType.WhiteSpace))
                {
                    return(previous);
                }
            }

            return(null);
        }
Example #6
0
        /// <summary>
        /// Checks a type to determine whether it should use one of the built-in types.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="parentElement">The parent element.</param>
        private void CheckBuiltInType(TypeToken type, Element parentElement)
        {
            Param.AssertNotNull(type, "type");
            Param.AssertNotNull(parentElement, "parentElement");

            if (!type.IsGeneric)
            {
                for (int i = 0; i < this.builtInTypes.Length; ++i)
                {
                    string[] builtInType = this.builtInTypes[i];

                    if (type.MatchTokens(builtInType[ShortNameIndex]) ||
                        type.MatchTokens("System", ".", builtInType[ShortNameIndex]))
                    {
                        // If the previous token is an equals sign, then this is a using alias directive. For example:
                        // using SomeAlias = System.String;
                        bool usingAliasDirective = false;
                        for (LexicalElement previous = type.FindPreviousLexicalElement(); previous != null; previous = previous.FindPreviousLexicalElement())
                        {
                            if (previous.LexicalElementType != LexicalElementType.Comment &&
                                previous.LexicalElementType != LexicalElementType.WhiteSpace &&
                                previous.LexicalElementType != LexicalElementType.EndOfLine)
                            {
                                if (previous.Text == "=")
                                {
                                    usingAliasDirective = true;
                                }

                                break;
                            }
                        }

                        if (!usingAliasDirective)
                        {
                            this.Violation(
                                Rules.UseBuiltInTypeAlias,
                                new ViolationContext(parentElement, type.LineNumber, builtInType[AliasIndex], builtInType[ShortNameIndex], builtInType[LongNameIndex]),
                                (c, o) =>
                            {
                                // Insert a new type token with the correct aliased version of the type.
                                CsDocument document = type.Document;
                                TypeToken aliasType = document.CreateTypeToken(document.CreateLiteralToken(builtInType[AliasIndex]));
                                document.Replace(type, aliasType);
                            });
                        }

                        break;
                    }
                }
            }
        }