/// <summary> /// Extracts a variable from the clause. /// </summary> /// <param name="firstToken">The first token of the variable.</param> /// <param name="allowTypelessVariable">Indicates whether to allow a variable with no type defined.</param> /// <param name="onlyTypelessVariable">Indicates whether to only get a typeless variable.</param> /// <returns>Returns the variable.</returns> protected static QueryClauseVariable ExtractQueryVariable(Token firstToken, bool allowTypelessVariable, bool onlyTypelessVariable) { Param.RequireNotNull(firstToken, "firstToken"); Param.Ignore(allowTypelessVariable); Param.Ignore(onlyTypelessVariable); if (onlyTypelessVariable || !firstToken.Is(TokenType.Type)) { // In this case there is no type, only an identifier. return new QueryClauseVariable(null, firstToken.Text, firstToken.Location, firstToken.Generated); } else { TypeToken type = (TypeToken)firstToken; // Attempt to get the identifier token coming after the type token. Token identifier = firstToken.FindNextSiblingToken(); if (identifier == null || identifier.TokenType != TokenType.Literal) { CsLanguageService.Debug.Assert(allowTypelessVariable, "The clause does not allow typeless variables. The parser should have thrown syntax exception already."); return new QueryClauseVariable(null, type.Text, type.Location, type.Generated); } else { // There is a type and an identifier. return new QueryClauseVariable(type, identifier.Text, CodeUnit.JoinLocations(type, identifier), type.Generated || identifier.Generated); } } }
/// <summary> /// Initializes a new instance of the LiteralExpression class. /// </summary> /// <param name="proxy">Proxy object for the expression.</param> /// <param name="token">The literal token.</param> internal LiteralExpression(CodeUnitProxy proxy, Token token) : base(proxy, ExpressionType.Literal) { Param.AssertNotNull(proxy, "proxy"); Param.AssertNotNull(token, "token"); this.token.Value = token; }
/// <summary> /// Visits one code unit in the document. /// </summary> /// <param name="codeUnit">The item being visited.</param> /// <param name="parentElement">The parent element, if any.</param> /// <param name="parentStatement">The parent statement, if any.</param> /// <param name="parentExpression">The parent expression, if any.</param> /// <param name="parentClause">The parent query clause, if any.</param> /// <param name="parentToken">The parent token, if any.</param> /// <param name="settings">The settings.</param> /// <returns>Returns true to continue, or false to stop the walker.</returns> private bool VisitCodeUnit( CodeUnit codeUnit, Element parentElement, Statement parentStatement, Expression parentExpression, QueryClause parentClause, Token parentToken, Settings settings) { Param.AssertNotNull(codeUnit, "codeUnit"); Param.Ignore(parentElement, parentStatement, parentExpression, parentClause, parentToken); Param.AssertNotNull(settings, "settings"); if (codeUnit.CodeUnitType == CodeUnitType.Element) { return this.VisitElement((Element)codeUnit, settings); } else if (codeUnit.CodeUnitType == CodeUnitType.Expression) { return this.VisitExpression((Expression)codeUnit, parentElement); } else if (codeUnit.Is(LexicalElementType.Token)) { Token token = (Token)codeUnit; if (token.TokenType == TokenType.Type && !token.Parent.Is(TokenType.Type)) { // Check that the type is using the built-in types, if applicable. this.CheckBuiltInType((TypeToken)token, parentElement); } else if (token.TokenType == TokenType.String) { // Check that the string is not using the empty string "" syntax. this.CheckEmptyString(token, parentElement); } } else if (codeUnit.Is(PreprocessorType.Region)) { this.CheckRegion((RegionDirective)codeUnit, parentElement, settings); } else if (codeUnit.Is(LexicalElementType.Comment)) { this.CheckForEmptyComments((Comment)codeUnit, parentElement); } return !this.Cancel; }
/// <summary> /// Fixes the error. /// </summary> /// <param name="violationContext">Context for the violation.</param> /// <param name="token">The token that is missing a prefix.</param> private void FixPrefixLocalCallsWithThisViolation(ViolationContext violationContext, Token token) { Param.Ignore(violationContext); Param.AssertNotNull(token, "token"); CsDocument document = token.Document; // Find the item that is going to get replaced. CodeUnit itemToReplace = token; LiteralExpression parent = token.Parent as LiteralExpression; if (parent != null) { itemToReplace = parent; } // Record the location of this item. CodeUnitLocationMarker marker = document.GetCodeUnitLocationMarker(itemToReplace); // If the token is not already wrapped by a literal expression, create one. LiteralExpression rightHandSide = parent; if (rightHandSide == null) { rightHandSide = document.CreateLiteralExpression(token); } // Create the left-hand side and the expression. LiteralExpression leftHandSide = document.CreateLiteralExpression(document.CreateThisToken()); MemberAccessExpression expression = document.CreateMemberAccessExpression(leftHandSide, rightHandSide); // Insert the member access expression in the same location as the previous literal. document.Insert(expression, marker); }
/// <summary> /// Parses the given literal token. /// </summary> /// <param name="token">The literal token node.</param> /// <param name="expression">The expression that contains the token.</param> /// <param name="parentExpression">The parent of the expression that contains the token.</param> /// <param name="parentElement">The element that contains the expression.</param> /// <param name="parentClass">The class that the element belongs to.</param> /// <param name="members">The collection of members of the parent class.</param> private void CheckClassMemberRulesForLiteralToken( Token token, Expression expression, Expression parentExpression, Element parentElement, ClassBase parentClass, Dictionary<string, List<Element>> members) { Param.AssertNotNull(token, "token"); Param.AssertNotNull(expression, "expression"); Param.Ignore(parentExpression); Param.AssertNotNull(parentElement, "parentElement"); Param.Ignore(parentClass); Param.Ignore(members); // Skip types. We only care about named members. if (token.TokenType != TokenType.Type) { // If the name starts with a dot, ignore it. if (!token.Text.StartsWith(".", StringComparison.Ordinal)) { if (token.Text == "base" && parentExpression != null) { // An item is only allowed to start with base if there is an implementation of the // item in the local class and the caller is trying to explicitly call the base // class implementation instead of the local class implementation. Extract the name // of the item being accessed. Token name = ReadabilityRules.ExtractBaseClassMemberName(parentExpression, token); if (name != null) { ICollection<Element> matches = ReadabilityRules.FindClassMember(name.Text, parentClass, members, true); // Check to see if there is a non-static match. bool found = false; if (matches != null) { foreach (Element match in matches) { if (!match.ContainsModifier(TokenType.Static)) { found = true; break; } } } if (!found) { this.AddViolation(parentElement, name.LineNumber, Rules.DoNotPrefixCallsWithBaseUnlessLocalImplementationExists, name); } } } else if (token.Text != "this") { // Check whether this word should really start with this. this.CheckWordUsageAgainstClassMemberRules( token.Text, token, token.LineNumber, expression, parentElement, parentClass, members); } } } }
/// <summary> /// Determines whether the given word is the name of a local variable. /// </summary> /// <param name="word">The name to check.</param> /// <param name="item">The token containing the word.</param> /// <param name="parent">The expression that the word appears in.</param> /// <returns>True if the word is the name of a local variable, false if not.</returns> private static bool IsLocalMember(string word, Token item, Expression parent) { Param.AssertValidString(word, "word"); Param.AssertNotNull(item, "item"); Param.AssertNotNull(parent, "parent"); CodeUnit p = parent; while (p != null) { // Check to see if the name matches a local variable. if (ReadabilityRules.ContainsVariable(p.Variables, word, item)) { return true; } // If the parent is an element (other than an accessor), do not look any higher up the stack than this. if (p.CodeUnitType == CodeUnitType.Element && !p.Is(ElementType.Accessor)) { break; } // Check to see whether the variable is defined within the parent. p = p.Parent; } return false; }
/// <summary> /// Extracts the name of the member being called from the base class. /// </summary> /// <param name="parentExpression">The expression containing the tokens.</param> /// <param name="baseToken">The 'base' keyword token.</param> /// <returns>Returns the name of the member or null if there is no member name.</returns> private static Token ExtractBaseClassMemberName(Expression parentExpression, Token baseToken) { Param.AssertNotNull(parentExpression, "parentExpression"); Param.AssertNotNull(baseToken, "baseTokenNode"); Debug.Assert(baseToken.TokenType == TokenType.Base, "Incorrect token type."); bool foundMemberAccessSymbol = false; for (Token next = baseToken.FindNextToken(); next != null; next = next.FindNextToken()) { if (foundMemberAccessSymbol) { if (next.TokenType == TokenType.Literal) { return next; } else { break; } } else { if (next.TokenType == TokenType.OperatorSymbol) { if (next.Is(OperatorType.MemberAccess)) { foundMemberAccessSymbol = true; } else { break; } } else { break; } } } return null; }
/// <summary> /// Checks a open bracket for spacing. /// </summary> /// <param name="token">The token to check.</param> private void CheckOpenSquareBracket(Token token) { Param.AssertNotNull(token, "token"); // Open brackets should be never be preceded by whitespace. LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem != null) { if (previousItem.LexicalElementType == LexicalElementType.WhiteSpace || previousItem.LexicalElementType == LexicalElementType.EndOfLine) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.OpeningSquareBracketsMustBeSpacedCorrectly); } } // Open brackets should never be followed by whitespace. LexicalElement nextItem = token.FindNextLexicalElement(); if (nextItem != null && (nextItem.LexicalElementType == LexicalElementType.WhiteSpace || nextItem.LexicalElementType == LexicalElementType.EndOfLine)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.OpeningSquareBracketsMustBeSpacedCorrectly); } }
/// <summary> /// Checks a label colon for spacing. /// </summary> /// <param name="token">The token to check.</param> private void CheckLabelColon(Token token) { Param.AssertNotNull(token, "token"); // A colon should always be followed by whitespace, but never preceded by whitespace. LexicalElement nextItem = token.FindNextLexicalElement(); if (nextItem == null) { if (nextItem.LexicalElementType != LexicalElementType.WhiteSpace && nextItem.LexicalElementType != LexicalElementType.EndOfLine) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ColonsMustBeSpacedCorrectly); } } LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem != null) { if (previousItem.LexicalElementType == LexicalElementType.WhiteSpace || previousItem.LexicalElementType == LexicalElementType.EndOfLine || previousItem.Is(CommentType.SingleLineComment) || previousItem.Is(CommentType.MultilineComment)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ColonsMustBeSpacedCorrectly); } } }
private void CheckUnsafeAccessSymbols(Token token, bool type) { Param.AssertNotNull(token, "token"); Param.Ignore(type); // In a type declaration, the symbol must have whitespace on the right but // not on the left. If this is not a type declaration, the opposite is true. if (type) { // The symbol should be followed by whitespace. It // can also be followed by a closing paren or a closing bracket, // or another token of the same type. LexicalElement nextItem = token.FindNextLexicalElement(); if (nextItem != null) { if (nextItem.LexicalElementType != LexicalElementType.WhiteSpace && nextItem.LexicalElementType != LexicalElementType.EndOfLine && !nextItem.Is(TokenType.OpenParenthesis) && !nextItem.Is(TokenType.OpenSquareBracket) && !nextItem.Is(TokenType.CloseParenthesis) && !nextItem.Is(token.TokenType)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.DereferenceAndAccessOfSymbolsMustBeSpacedCorrectly); } } // The symbol must not be preceded by whitespace. LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem != null) { if (previousItem.LexicalElementType == LexicalElementType.WhiteSpace || previousItem.LexicalElementType == LexicalElementType.EndOfLine || previousItem.Is(CommentType.SingleLineComment) || previousItem.Is(CommentType.MultilineComment)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.DereferenceAndAccessOfSymbolsMustBeSpacedCorrectly); } } } else { // The symbol should be preceded by whitespace. It // can also be preceded by an open paren or an open bracket, or // another token of the same type. LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem != null) { if (previousItem.LexicalElementType != LexicalElementType.WhiteSpace && previousItem.LexicalElementType != LexicalElementType.EndOfLine && !previousItem.Is(TokenType.OpenParenthesis) && !previousItem.Is(TokenType.OpenSquareBracket) && !previousItem.Is(TokenType.CloseParenthesis) && !previousItem.Is(token.TokenType)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.DereferenceAndAccessOfSymbolsMustBeSpacedCorrectly); } } // The symbol must not be followed by whitespace. LexicalElement nextItem = token.FindNextLexicalElement(); if (nextItem == null) { if (nextItem.LexicalElementType == LexicalElementType.WhiteSpace || nextItem.LexicalElementType == LexicalElementType.EndOfLine || nextItem.Is(CommentType.SingleLineComment) || nextItem.Is(CommentType.MultilineComment)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.DereferenceAndAccessOfSymbolsMustBeSpacedCorrectly); } } } }
/// <summary> /// Checks a positive sign for spacing. /// </summary> /// <param name="token">The token to check.</param> private void CheckPositiveSign(Token token) { Param.AssertNotNull(token, "token"); // A positive sign should be preceded by whitespace. It // can also be preceded by an open paren or an open bracket. LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem != null) { if (previousItem.LexicalElementType != LexicalElementType.WhiteSpace && previousItem.LexicalElementType != LexicalElementType.EndOfLine && !previousItem.Is(TokenType.OpenParenthesis) && !previousItem.Is(TokenType.OpenSquareBracket) && !previousItem.Is(TokenType.CloseParenthesis)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.PositiveSignsMustBeSpacedCorrectly); } } LexicalElement nextItem = token.FindNextLexicalElement(); if (nextItem != null) { if (nextItem.LexicalElementType == LexicalElementType.WhiteSpace || nextItem.LexicalElementType == LexicalElementType.EndOfLine || nextItem.Is(CommentType.SingleLineComment) || nextItem.Is(CommentType.MultilineComment)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.PositiveSignsMustBeSpacedCorrectly); } } }
/// <summary> /// Checks an increment or decrement sign for spacing. /// </summary> /// <param name="token">The token to check.</param> private void CheckIncrementDecrement(Token token) { Param.AssertNotNull(token, "token"); // Increment and decrement symbols should have whitespace on only one side. The non-whitespace // side is also allowed to butt up against a bracket or a parenthesis, however. bool before = false; bool after = false; LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem == null) { before = true; } else { if (previousItem.LexicalElementType == LexicalElementType.WhiteSpace || previousItem.LexicalElementType == LexicalElementType.EndOfLine || previousItem.Is(CommentType.SingleLineComment) || previousItem.Is(CommentType.MultilineComment)) { before = true; } } LexicalElement nextItem = token.FindNextLexicalElement(); if (nextItem == null) { after = true; } else { if (nextItem.LexicalElementType == LexicalElementType.WhiteSpace || nextItem.LexicalElementType == LexicalElementType.EndOfLine || nextItem.Is(CommentType.SingleLineComment) || nextItem.Is(CommentType.MultilineComment)) { after = true; } } // If there is no whitespace on either side, then make sure that at least one of the sides // is touching a square bracket or a parenthesis. The right side of the symbol is also // allowed to be up against a comma or a semicolon. if (!before && !after) { if (previousItem != null && (previousItem.Is(TokenType.OpenSquareBracket) || previousItem.Is(TokenType.OpenParenthesis))) { return; } if (nextItem != null) { if (nextItem.Is(TokenType.CloseSquareBracket) || nextItem.Is(TokenType.CloseParenthesis) || nextItem.Is(TokenType.Comma) || nextItem.Is(TokenType.Semicolon)) { return; } } // This is a violation. this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.IncrementDecrementSymbolsMustBeSpacedCorrectly); } else if (before && after) { // There is whitespace on both sides. this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.IncrementDecrementSymbolsMustBeSpacedCorrectly); } }
/// <summary> /// Checks a member access symbol for spacing. /// </summary> /// <param name="root">The container to parse.</param> /// <param name="token">The token to check.</param> private void CheckMemberAccessSymbol(CodeUnit root, Token token) { Param.AssertNotNull(root, "root"); Param.AssertNotNull(token, "token"); // Member access symbols should not have any whitespace on either side. LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem == null) { if (previousItem.LexicalElementType == LexicalElementType.WhiteSpace || previousItem.LexicalElementType == LexicalElementType.EndOfLine || previousItem.Is(CommentType.SingleLineComment) || previousItem.Is(CommentType.MultilineComment)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.MemberAccessSymbolsMustBeSpacedCorrectly); } } LexicalElement nextItem = token.FindNextLexicalElement(); if (nextItem == null) { if (nextItem.LexicalElementType == LexicalElementType.WhiteSpace || nextItem.LexicalElementType == LexicalElementType.EndOfLine || nextItem.Is(CommentType.SingleLineComment) || nextItem.Is(CommentType.MultilineComment)) { // Make sure the previous token is not the operator keyword. if (previousItem != null) { for (LexicalElement item = previousItem.FindPreviousLexicalElement(); item != null; item = item.FindPreviousLexicalElement()) { if (item.Is(TokenType.Operator)) { return; } else if (item.LexicalElementType != LexicalElementType.WhiteSpace && item.LexicalElementType != LexicalElementType.EndOfLine && !item.Is(CommentType.SingleLineComment) && !item.Is(CommentType.MultilineComment)) { break; } } } this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.MemberAccessSymbolsMustBeSpacedCorrectly); } } }
/// <summary> /// Checks a nullable type symbol for spacing. /// </summary> /// <param name="token">The token to check.</param> private void CheckNullableTypeSymbol(Token token) { Param.AssertNotNull(token, "token"); // Nullable type symbols should never be preceded by whitespace. LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem != null && (previousItem.LexicalElementType == LexicalElementType.WhiteSpace || previousItem.LexicalElementType == LexicalElementType.EndOfLine)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.NullableTypeSymbolsMustNotBePrecededBySpace); } }
/// <summary> /// Checks a closing attribute bracket for spacing. /// </summary> /// <param name="token">The token to check.</param> private void CheckAttributeTokenCloseBracket(Token token) { Param.AssertNotNull(token, "token"); // Closing attribute brackets should be never be preceded by whitespace. LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem != null) { if (previousItem.LexicalElementType == LexicalElementType.WhiteSpace || previousItem.LexicalElementType == LexicalElementType.EndOfLine) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ClosingAttributeBracketsMustBeSpacedCorrectly); } } }
private void CheckOpenParen(CodeUnit root, Token token) { Param.AssertNotNull(root, "root"); Param.AssertNotNull(token, "token"); bool firstOnLine = false; bool lastOnLine = false; // Open parenthesis should never be preceded by whitespace unless it is the // first thing on the line or it follows a keyword or it follows a symbol or a number. LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem != null) { if (previousItem.LexicalElementType == LexicalElementType.WhiteSpace) { for (LexicalElement item = previousItem.FindPreviousLexicalElement(); item != null; item = item.FindPreviousLexicalElement()) { if (item.LexicalElementType == LexicalElementType.WhiteSpace) { continue; } else if (item.LexicalElementType == LexicalElementType.EndOfLine) { firstOnLine = true; break; } else if ( item.Is(TokenType.Case) || item.Is(TokenType.Catch) || item.Is(TokenType.CloseSquareBracket) || item.Is(TokenType.Comma) || item.Is(TokenType.Equals) || item.Is(TokenType.Fixed) || item.Is(TokenType.For) || item.Is(TokenType.Foreach) || item.Is(TokenType.From) || ////item.Is(TokenType.Goto) || item.Is(TokenType.Group) || item.Is(TokenType.If) || item.Is(TokenType.In) || item.Is(TokenType.Into) || item.Is(TokenType.Join) || item.Is(TokenType.Let) || item.Is(TokenType.Lock) || item.Is(CommentType.MultilineComment) || ////item.Is(TokenType.New) || item.Is(TokenType.Number) || item.Is(TokenType.OperatorSymbol) || item.Is(TokenType.OpenCurlyBracket) || item.Is(TokenType.OrderBy) || item.Is(TokenType.Return) || item.Is(TokenType.Select) || item.Is(TokenType.Semicolon) || ////item.Is(CommentType.SingleLineComment) || item.Is(TokenType.Switch) || item.Is(TokenType.Throw) || item.Is(TokenType.Using) || item.Is(TokenType.Where) || item.Is(TokenType.While) || item.Is(TokenType.WhileDo) || item.Is(TokenType.Yield)) { break; } else { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.OpeningParenthesisMustBeSpacedCorrectly); } } } } // Open parens should never be followed by whitespace unless // it is the last thing on the line. LexicalElement next = token.FindPreviousLexicalElement(); if (next != null && (next.LexicalElementType == LexicalElementType.WhiteSpace || next.LexicalElementType == LexicalElementType.EndOfLine)) { // Look to see if there is any non whitespace character // on this line other than a comment. for (LexicalElement item = next.FindNextLexicalElement(); item != null; item = item.FindNextLexicalElement()) { if (item.LexicalElementType == LexicalElementType.EndOfLine) { lastOnLine = true; break; } else if (item.LexicalElementType != LexicalElementType.WhiteSpace && !item.Is(CommentType.SingleLineComment) && !item.Is(CommentType.MultilineComment)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.OpeningParenthesisMustBeSpacedCorrectly); break; } } } // Open parens cannot be the only thing on the line. if (firstOnLine && lastOnLine) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.OpeningParenthesisMustBeSpacedCorrectly); } }
private void CheckCloseParen(CodeUnit root, Token token) { Param.AssertNotNull(root, "root"); Param.AssertNotNull(token, "token"); // Close parens should never be preceded by whitespace. LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem != null && (previousItem.LexicalElementType == LexicalElementType.WhiteSpace || previousItem.LexicalElementType == LexicalElementType.EndOfLine)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ClosingParenthesisMustBeSpacedCorrectly); } // Find out what comes after the closing paren. LexicalElement nextItem = token.FindNextLexicalElement(); LexicalElement nextNextItem = nextItem == null ? null : nextItem.FindNextLexicalElement(); if (nextItem != null) { if (token.Parent is CastExpression) { // There should not be any whitespace after the closing parenthesis in a cast expression. if (nextItem.LexicalElementType == LexicalElementType.WhiteSpace) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ClosingParenthesisMustBeSpacedCorrectly); } } else if (nextItem.Is(TokenType.LabelColon) || (nextNextItem != null && nextNextItem.Is(TokenType.LabelColon))) { // If the next token is a colon, it's allowed to omit the whitespace only if we are in a switch\case statement. bool followsCase = false; for (LexicalElement item = token.FindPreviousLexicalElement(); item != null; item = item.FindPreviousLexicalElement()) { if (item.LexicalElementType == LexicalElementType.EndOfLine) { break; } else if (item.Is(TokenType.Case)) { followsCase = true; break; } } if ((followsCase && nextItem.LexicalElementType == LexicalElementType.WhiteSpace) || (!followsCase && nextItem.LexicalElementType != LexicalElementType.WhiteSpace)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ClosingParenthesisMustBeSpacedCorrectly); } } else if (nextItem.LexicalElementType == LexicalElementType.WhiteSpace) { if (nextNextItem != null) { // Make sure that the character just after the whitespace is not a paren, bracket, a comma, or a semicolon. for (LexicalElement item = nextNextItem; item != null; item = item.FindNextLexicalElement()) { if (IsAllowedAfterClosingParenthesis(item)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ClosingParenthesisMustBeSpacedCorrectly); } else if (item.LexicalElementType != LexicalElementType.WhiteSpace) { break; } } } } else { // For all other types, the parenthesis must be followed by whitespace, unless the next character is a paren, bracket, comma, or a semicolon. if (nextItem.LexicalElementType != LexicalElementType.EndOfLine && !IsAllowedAfterClosingParenthesis(nextItem)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ClosingParenthesisMustBeSpacedCorrectly); } } } }
/// <summary> /// Checks a unary symbol for spacing. /// </summary> /// <param name="token">The token to check.</param> private void CheckUnarySymbol(Token token) { Param.AssertNotNull(token, "token"); // These symbols should be preceded by whitespace but not followed by whitespace. They can // also be preceded by an open paren or an open square bracket. LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem != null) { if (previousItem.LexicalElementType != LexicalElementType.WhiteSpace && previousItem.LexicalElementType != LexicalElementType.EndOfLine && !previousItem.Is(TokenType.OpenParenthesis) && !previousItem.Is(TokenType.OpenSquareBracket)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.SymbolsMustBeSpacedCorrectly, token.Text); } } LexicalElement nextItem = token.FindNextLexicalElement(); if (nextItem == null) { if (nextItem.LexicalElementType == LexicalElementType.WhiteSpace || nextItem.LexicalElementType == LexicalElementType.EndOfLine || nextItem.Is(CommentType.SingleLineComment) || nextItem.Is(CommentType.MultilineComment)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.SymbolsMustBeSpacedCorrectly, token.Text); } } }
private void CheckCloseSquareBracket(CodeUnit root, Token token) { Param.AssertNotNull(root, "root"); Param.AssertNotNull(token, "token"); // Close brackets should never be preceded by whitespace. LexicalElement previousItem = token.FindNextLexicalElement(); if (previousItem != null && (previousItem.LexicalElementType == LexicalElementType.WhiteSpace || previousItem.LexicalElementType == LexicalElementType.EndOfLine)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ClosingSquareBracketsMustBeSpacedCorrectly); } // Close brackets should be followed either by whitespace, a bracket, // a paren, a semicolon, a comma, a period, or an increment or decrement symbol. LexicalElement nextItem = token.FindNextLexicalElement(); if (nextItem != null) { if (nextItem.LexicalElementType != LexicalElementType.WhiteSpace && nextItem.LexicalElementType != LexicalElementType.EndOfLine && !nextItem.Is(TokenType.CloseParenthesis) && !nextItem.Is(TokenType.OpenParenthesis) && // someDictionary["Test"](); !nextItem.Is(TokenType.CloseSquareBracket) && // someIndexer[someArray[1]] = 2; !nextItem.Is(TokenType.OpenSquareBracket) && // someArray[1][2] = 2; !nextItem.Is(TokenType.Semicolon) && !nextItem.Is(TokenType.Comma) && !nextItem.Is(TokenType.CloseGenericBracket) && nextItem.Text != "++" && nextItem.Text != "--" && !nextItem.Text.StartsWith(".", StringComparison.Ordinal)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ClosingSquareBracketsMustBeSpacedCorrectly); } if (nextItem.LexicalElementType == LexicalElementType.WhiteSpace) { // If this is followed by whitespace, make sure that the character just // after the whitespace is not a paren, bracket, comma, or semicolon. for (LexicalElement item = nextItem.FindNextLexicalElement(); item != null; item = item.FindNextLexicalElement()) { if (item.Is(TokenType.CloseParenthesis) || item.Is(TokenType.OpenParenthesis) || item.Is(TokenType.CloseSquareBracket) || item.Is(TokenType.OpenSquareBracket) || item.Is(TokenType.Semicolon) || item.Is(TokenType.Comma)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ClosingSquareBracketsMustBeSpacedCorrectly); } else if (item.LexicalElementType != LexicalElementType.WhiteSpace) { break; } } } } }
/// <summary> /// Checks a keyword that should be followed by a space. /// </summary> /// <param name="token">The token to check.</param> private void CheckKeywordWithSpace(Token token) { Param.AssertNotNull(token, "token"); // Keywords must be followed by a space before the open parenthesis. // Sometimes keywords appear within attributes and are allowed to be // followed immediately by an attribute colon. LexicalElement temp = token.FindNextLexicalElement(); if (temp == null || (temp.LexicalElementType != LexicalElementType.WhiteSpace && temp.LexicalElementType != LexicalElementType.EndOfLine && !temp.Is(TokenType.Semicolon) && !temp.Is(TokenType.AttributeColon))) { this.AddViolation( token.FindParentElement(), token.LineNumber, Rules.KeywordsMustBeSpacedCorrectly, token.Text); } }
/// <summary> /// Determines whether a matching local variable is contained in the given variable list. /// </summary> /// <param name="variables">The variable list.</param> /// <param name="word">The variable name to check.</param> /// <param name="item">The token containing the variable name.</param> /// <returns>Returns true if there is a matching local variable.</returns> private static bool ContainsVariable(VariableCollection variables, string word, Token item) { Param.AssertNotNull(variables, "variables"); Param.AssertValidString(word, "word"); Param.AssertNotNull(item, "item"); IVariable variable = variables[word]; if (variable != null) { // Make sure the variable appears before the word. if (variable.Location.LineNumber < item.LineNumber) { return true; } else if (variable.Location.LineNumber == item.LineNumber) { if (variable.Location.StartPoint.IndexOnLine <= item.Location.StartPoint.IndexOnLine) { return true; } } } return false; }
/// <summary> /// Checks a keyword that should not be followed by a space. /// </summary> /// <param name="root">The container to parse.</param> /// <param name="token">The token to check.</param> private void CheckKeywordWithoutSpace(CodeUnit root, Token token) { Param.AssertNotNull(root, "root"); Param.AssertNotNull(token, "token"); // Keywords must not contain any space before the open parenthesis. LexicalElement temp = token.FindNextLexicalElement(); if (temp != null && (temp.LexicalElementType == LexicalElementType.WhiteSpace || temp.LexicalElementType == LexicalElementType.EndOfLine)) { // Make sure the next non-whitespace character is not an open parenthesis. for (LexicalElement nextNonWhitespaceItem = temp.FindNextLexicalElement(); nextNonWhitespaceItem != null; nextNonWhitespaceItem = nextNonWhitespaceItem.FindNextLexicalElement()) { if (nextNonWhitespaceItem.Is(TokenType.OpenParenthesis)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.KeywordsMustBeSpacedCorrectly, token.Text); break; } else if (nextNonWhitespaceItem.LexicalElementType != LexicalElementType.WhiteSpace && nextNonWhitespaceItem.LexicalElementType != LexicalElementType.EndOfLine) { break; } } } }
/// <summary> /// Determines whether the given token is preceded by a member access symbol. /// </summary> /// <param name="literalToken">The token to check.</param> /// <returns>Returns true if the token is preceded by a member access symbol.</returns> private static bool IsLiteralTokenPrecededByMemberAccessSymbol(Token literalToken) { Param.AssertNotNull(literalToken, "literalToken"); // Get the previous non-whitespace item. LexicalElement previousItem = ReadabilityRules.GetPreviousNonWhitespaceItem(literalToken); if (previousItem != null) { if (previousItem.Is(OperatorType.MemberAccess) || previousItem.Is(OperatorType.Pointer) || previousItem.Is(OperatorType.QualifiedAlias)) { return true; } } return false; }
/// <summary> /// Checks the spacing around a 'new' keyword. /// </summary> /// <param name="root">The token container list.</param> /// <param name="token">The token to check.</param> private void CheckNewKeywordSpacing(CodeUnit root, Token token) { Param.AssertNotNull(root, "root"); Param.AssertNotNull(token, "token"); // The keywords must be followed by a space, unless the next token is an opening square bracket, in which case // there should be no space. LexicalElement temp = token.FindNextLexicalElement(); if (temp != null) { if (temp.LexicalElementType == LexicalElementType.WhiteSpace || temp.LexicalElementType == LexicalElementType.EndOfLine) { // The keyword is followed by whitespace. Make sure the next non-whitespace character is not an opening bracket. for (LexicalElement nextNonWhitespaceItem = temp.FindNextLexicalElement(); nextNonWhitespaceItem != null; nextNonWhitespaceItem = nextNonWhitespaceItem.FindNextLexicalElement()) { if (nextNonWhitespaceItem.Is(TokenType.OpenSquareBracket)) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.CodeMustNotContainSpaceAfterNewKeywordInImplicitlyTypedArrayAllocation); break; } else if (nextNonWhitespaceItem.LexicalElementType != LexicalElementType.WhiteSpace && nextNonWhitespaceItem.LexicalElementType != LexicalElementType.EndOfLine) { break; } } } else if (!temp.Is(TokenType.OpenSquareBracket)) { // The keyword is not followed by whitespace. this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.KeywordsMustBeSpacedCorrectly, token.Text); } } }
/// <summary> /// Checks a word to see if it should start with this. or base. /// </summary> /// <param name="word">The word text to check.</param> /// <param name="item">The word being checked.</param> /// <param name="line">The line that the word appears on.</param> /// <param name="expression">The expression the word appears within.</param> /// <param name="parentElement">The element that contains the word.</param> /// <param name="parentClass">The parent class that this element belongs to.</param> /// <param name="members">The collection of members of the parent class.</param> private void CheckWordUsageAgainstClassMemberRules( string word, Token item, int line, Expression expression, Element parentElement, ClassBase parentClass, Dictionary<string, List<Element>> members) { Param.AssertValidString(word, "word"); Param.AssertNotNull(item, "item"); Param.AssertGreaterThanZero(line, "line"); Param.AssertNotNull(expression, "expression"); Param.AssertNotNull(parentElement, "parentElement"); Param.Ignore(parentClass); Param.Ignore(members); // If there is a local variable with the same name, or if the item we're checking is within the left-hand side // of an object initializer expression, then ignore it. if (!IsLocalMember(word, item, expression) && !IsObjectInitializerLeftHandSideExpression(expression)) { // Determine if this is a member of our class. Element foundMember = null; ICollection<Element> classMembers = ReadabilityRules.FindClassMember(word, parentClass, members, false); if (classMembers != null) { foreach (Element classMember in classMembers) { if (classMember.ContainsModifier(TokenType.Static) || (classMember.ElementType == ElementType.Field && ((Field)classMember).Const)) { // There is a member with a matching name that is static or is a const field. In this case, // ignore the issue and quit. foundMember = null; break; } else if (classMember.ElementType != ElementType.Class && classMember.ElementType != ElementType.Struct && classMember.ElementType != ElementType.Delegate && classMember.ElementType != ElementType.Enum) { // Found a matching member. if (foundMember == null) { foundMember = classMember; } } } if (foundMember != null) { if (foundMember.ElementType == ElementType.Property) { // If the property's name and type are the same, then this is not a violation. // In this case, the type is being accessed, not the property. Property property = (Property)foundMember; if (property.ReturnType.Text != property.Name) { this.Violation<Token>( Rules.PrefixLocalCallsWithThis, new ViolationContext(parentElement, line, word), this.FixPrefixLocalCallsWithThisViolation, item); } } else { this.Violation<Token>( Rules.PrefixLocalCallsWithThis, new ViolationContext(parentElement, line, word), this.FixPrefixLocalCallsWithThisViolation, item); } } } } }
private void CheckSemicolonAndComma(Token token) { Param.AssertNotNull(token, "token"); bool comma = false; if (token.Text == ",") { comma = true; } else { Debug.Assert(token.Text == ";", "The token should either be a comma or a semicolon"); } // There is a special case here where we allow <,,> [,,] or (;;), or variations thereof. // In these cases, there should be no spaces around the comma or semicolon. string[] open = new string[] { "[", "<" }; string[] close = new string[] { "]", ">" }; if (!comma) { open = new string[] { "(" }; close = new string[] { ")" }; } bool specialCaseBackwards = true; bool specialCaseForwards = true; // Work backwards and look for the previous character on this line. bool found = false; LexicalElement item = token.FindPreviousLexicalElement(); if (item != null) { for (int i = 0; i < open.Length; ++i) { if (item.Text == open[i]) { found = true; break; } } if (!found) { if (item.Text == token.Text) { found = true; } else { specialCaseBackwards = false; } } } if (!found) { specialCaseBackwards = false; } // Work forwards and look for the next character on this line. found = false; item = token.FindNextLexicalElement(); if (item != null) { for (int i = 0; i < close.Length; ++i) { if (item.Text == close[i]) { found = true; break; } } if (!found) { if (item.Text == token.Text) { found = true; } else { specialCaseForwards = false; } } } if (!found) { specialCaseForwards = false; } if (!specialCaseBackwards) { LexicalElement previousItem = token.FindPreviousLexicalElement(); // Make sure this is not preceded by whitespace. if (previousItem != null && (previousItem.LexicalElementType == LexicalElementType.WhiteSpace || previousItem.LexicalElementType == LexicalElementType.EndOfLine)) { this.AddViolation( token.FindParentElement(), token.LineNumber, comma ? Rules.CommasMustBeSpacedCorrectly : Rules.SemicolonsMustBeSpacedCorrectly); } } if (!specialCaseForwards) { LexicalElement nextItem = token.FindNextLexicalElement(); // Make sure this is followed by whitespace or a close paren. if (nextItem != null && nextItem.LexicalElementType != LexicalElementType.WhiteSpace && nextItem.LexicalElementType != LexicalElementType.EndOfLine && !nextItem.Is(TokenType.CloseParenthesis)) { this.AddViolation( token.FindParentElement(), token.LineNumber, comma ? Rules.CommasMustBeSpacedCorrectly : Rules.SemicolonsMustBeSpacedCorrectly); } } }
/// <summary> /// Determines whether the statement is declaring a const field or variable. /// </summary> /// <param name="assignmentOperator">The assignment operator for the variable declaration.</param> /// <returns>Returns true if the statement is declaring a const, false otherwise.</returns> private static bool IsConstVariableDeclaration(Token assignmentOperator) { Param.Ignore(assignmentOperator); if (assignmentOperator != null && assignmentOperator.Text == "=") { // Work backwards until we find the keyword const, or have moved past the beginning of the statement. Token token = assignmentOperator.FindPreviousToken(); while (token != null) { if (token.TokenType == TokenType.CloseParenthesis || token.TokenType == TokenType.OpenParenthesis || token.TokenType == TokenType.OpenCurlyBracket || token.TokenType == TokenType.CloseCurlyBracket || token.TokenType == TokenType.Semicolon) { break; } else if (token.TokenType == TokenType.Const) { return true; } token = token.FindPreviousToken(); } } return false; }
/// <summary> /// Checks a symbol for spacing. /// </summary> /// <param name="root">The container to parse.</param> /// <param name="token">The token to check.</param> private void CheckSymbol(CodeUnit root, Token token) { Param.AssertNotNull(root, "root"); Param.AssertNotNull(token, "token"); // Symbols should have whitespace on both sides. LexicalElement previousItem = token.FindPreviousLexicalElement(); if (previousItem != null && previousItem.LexicalElementType != LexicalElementType.WhiteSpace && previousItem.LexicalElementType != LexicalElementType.EndOfLine) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.SymbolsMustBeSpacedCorrectly, token.Text); } LexicalElement nextItem = token.FindNextLexicalElement(); if (nextItem != null && nextItem.LexicalElementType != LexicalElementType.WhiteSpace && nextItem.LexicalElementType != LexicalElementType.EndOfLine) { // Make sure the previous token is not operator. if (previousItem != null) { for (LexicalElement item = previousItem.FindPreviousLexicalElement(); item != null; item = item.FindPreviousLexicalElement()) { if (item.Is(TokenType.Operator)) { return; } else if (item.LexicalElementType != LexicalElementType.WhiteSpace && item.LexicalElementType != LexicalElementType.EndOfLine && !item.Is(CommentType.SingleLineComment) && !item.Is(CommentType.MultilineComment) && item.LexicalElementType != LexicalElementType.PreprocessorDirective) { break; } } } this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.SymbolsMustBeSpacedCorrectly, token.Text); } }
/// <summary> /// Checks a string to determine whether it is using an incorrect empty string notation. /// </summary> /// <param name="text">The string to check.</param> /// <param name="parentElement">The parent element.</param> private void CheckEmptyString(Token text, Element parentElement) { Param.AssertNotNull(text, "text"); Param.AssertNotNull(parentElement, "parentElement"); Debug.Assert(text.TokenType == TokenType.String, "The token must be a string."); if (string.Equals(text.Text, "\"\"", StringComparison.Ordinal) || string.Equals(text.Text, "@\"\"", StringComparison.Ordinal)) { // Look at the previous token. If it is the 'case' keyword, then do not throw this // exception. It is illegal to write case: String.Empty and instead case: "" must be written. Token previousToken = text.FindPreviousToken(); if (previousToken == null || (previousToken.TokenType != TokenType.Case && !IsConstVariableDeclaration(previousToken))) { this.Violation( Rules.UseStringEmptyForEmptyStrings, new ViolationContext(parentElement, text.LineNumber), (c, o) => { // Fix the violation. CsDocument document = text.Document; // Create a member access expression which represents "string.Empty". var stringEmpty = document.CreateMemberAccessExpression(document.CreateLiteralExpression("string"), document.CreateLiteralExpression("Empty")); // The parent of the token should be a literal expression. if (text.Parent != null && text.Parent.Is(ExpressionType.Literal)) { document.Replace(text.Parent, stringEmpty); } else { Debug.Fail("Unhandled situation"); } }); } } }
/// <summary> /// Checks the operator keyword for spacing. /// </summary> /// <param name="token">The token to check.</param> private void CheckOperatorKeyword(Token token) { Param.AssertNotNull(token, "token"); // Operator keywords should be followed by whitespace. LexicalElement next = token.FindNextLexicalElement(); if (next != null && next.LexicalElementType != LexicalElementType.WhiteSpace) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.OperatorKeywordMustBeFollowedBySpace); } }