/// <summary> /// Initializes a new instance of the ElementWrapper class. /// </summary> /// <param name="element">The element to wrap.</param> public ElementWrapper(Element element) { Param.AssertNotNull(element, "element"); this.element = element; // Add any suppressed rules. this.AddRuleSuppressionsForElement(); }
/// <summary> /// Parses the body of an element that contains a list of statements as children. /// </summary> /// <param name="elementProxy">Proxy for the element being created.</param> /// <param name="element">The element to parse.</param> /// <param name="interfaceType">Indicates whether this type of statement container can appear in an interface.</param> /// <param name="unsafeCode">Indicates whether the code being parsed resides in an unsafe code block.</param> private void ParseStatementContainer(CodeUnitProxy elementProxy, Element element, bool interfaceType, bool unsafeCode) { Param.AssertNotNull(elementProxy, "elementProxy"); Param.AssertNotNull(element, "element"); Param.Ignore(interfaceType); Param.Ignore(unsafeCode); // Check to see if the item is unsafe. This is the case if the item's parent is unsafe, or if it // has the unsafe keyword itself. unsafeCode |= element.ContainsModifier(TokenType.Unsafe); // The next symbol must be an opening curly bracket. Symbol symbol = this.PeekNextSymbol(); if (symbol == null) { throw this.CreateSyntaxException(); } if (symbol.SymbolType == SymbolType.OpenCurlyBracket) { // Add the bracket token to the document. BracketToken openingBracket = (BracketToken)this.GetToken(elementProxy, TokenType.OpenCurlyBracket, SymbolType.OpenCurlyBracket); // Parse the contents of the element. BracketToken closingBracket = this.ParseStatementScope(elementProxy, unsafeCode); if (closingBracket == null) { // If we failed to get a closing bracket back, then there is a syntax // error in the document since there is an opening bracket with no matching // closing bracket. throw this.CreateSyntaxException(); } openingBracket.MatchingBracket = closingBracket; closingBracket.MatchingBracket = openingBracket; } else if (interfaceType && symbol.SymbolType == SymbolType.Semicolon) { // Add the semicolon to the document. this.GetToken(elementProxy, TokenType.Semicolon, SymbolType.Semicolon); } else { throw new SyntaxException(this.document, symbol.LineNumber); } }
/// <summary> /// Attempts to add an element to the partial elements service, if that element is partial. /// </summary> /// <param name="element">The element to add.</param> /// <returns>Returns true if the element was added, or false if the element was not partial.</returns> public bool TryAdd(Element element) { Param.RequireNotNull(element, "element"); // If the element is partial, add it to the partial elements list. if (element.ContainsModifier(TokenType.Partial) && element is ClassBase) { [email protected](); try { List<Element> elementList = null; // Get the partial element list for this element. string elementFullyQualifiedName = element.FullyQualifiedName; this.partialElements.TryGetValue(elementFullyQualifiedName, out elementList); if (elementList == null) { // Create a new partial element list for this element name. elementList = new List<Element>(); this.partialElements.Add(elementFullyQualifiedName, elementList); } else if (elementList.Count > 0) { // Make sure this elements is the same type as the item(s) already in the list. if (elementList[0].ElementType != element.ElementType) { throw new SyntaxException(element.Document, element.LineNumber); } } // Add the element to the list. elementList.Add(element); } finally { [email protected](); } return true; } return false; }
/// <summary> /// Processes the given query expression. /// </summary> /// <param name="element">The element that contains the expression.</param> /// <param name="queryExpression">The query expression.</param> private void CheckQueryExpression(Element element, QueryExpression queryExpression) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(queryExpression, "queryExpression"); QueryClause previousClause = null; bool clauseOnSameLine = false; bool clauseOnSeparateLine = false; this.ProcessQueryClauses( element, queryExpression, queryExpression, ref previousClause, ref clauseOnSameLine, ref clauseOnSeparateLine); }
private void CheckForEmptyComments(Comment comment, Element parentElement) { Param.AssertNotNull(comment, "comment"); Param.AssertNotNull(parentElement, "parentElement"); // Skip generated code. if (!comment.Generated) { // Check for the two comment types. if (comment.CommentType == CommentType.SingleLineComment) { this.CheckSingleLineComment(comment, parentElement); } else if (comment.CommentType == CommentType.MultilineComment) { this.CheckMultilineComment(comment, parentElement); } } }
/// <summary> /// Checks a region. /// </summary> /// <param name="region">The region to check.</param> /// <param name="parentElement">The parent element.</param> /// <param name="settings">The current settings.</param> private void CheckRegion(RegionDirective region, Element parentElement, Settings settings) { Param.AssertNotNull(region, "region"); Param.AssertNotNull(parentElement, "parentElement"); Param.AssertNotNull(settings, "settings"); if (settings.DoNotUseRegions) { if (!region.Generated && !region.IsGeneratedCodeRegion) { // There should not be any regions in the code. this.AddViolation(parentElement, region.LineNumber, Rules.DoNotUseRegions); } } }
/// <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; } } } }
/// <summary> /// Checks the given element. /// </summary> /// <param name="element">The element being visited.</param> /// <param name="settings">The current settings.</param> /// <returns>Returns true to continue, or false to stop the walker.</returns> private bool VisitElement(Element element, Settings settings) { Param.AssertNotNull(element, "element"); Param.Ignore(settings); this.CheckMethodParameters(element); this.CheckForRegionsInElement(element, settings); return true; }
/// <summary> /// Determines whether the given element contains any partial members. /// </summary> /// <param name="element">The element to check.</param> /// <returns>Returns true if the element contains at least one partial member.</returns> private bool ContainsPartialMembers(Element element) { Param.AssertNotNull(element, "element"); if (element.ElementType == ElementType.Class || element.ElementType == ElementType.Struct || element.ElementType == ElementType.Interface) { if (element.ContainsModifier(TokenType.Partial)) { return true; } } if (element.ElementType == ElementType.Document || element.ElementType == ElementType.Namespace || element.ElementType == ElementType.Class || element.ElementType == ElementType.Struct) { for (Element child = element.FindFirstChildElement(); child != null; child = child.FindNextSiblingElement()) { if (this.ContainsPartialMembers(child)) { return true; } } } return false; }
/// <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> /// Parses the given query clause. /// </summary> /// <param name="clause">The query clause.</param> /// <param name="parentExpression">The parent expression, if there is one.</param> /// <param name="parentElement">The element that contains the expressions.</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 CheckClassMemberRulesForQueryClause( QueryClause clause, Expression parentExpression, Element parentElement, ClassBase parentClass, Dictionary<string, List<Element>> members) { Param.AssertNotNull(clause, "clause"); Param.Ignore(parentExpression); Param.AssertNotNull(parentElement, "parentElement"); Param.AssertNotNull(parentClass, "parentClass"); Param.AssertNotNull(members, "members"); for (CodeUnit child = clause.FindFirstChild(); child != null; child = child.FindNextSibling()) { if (child.Is(CodeUnitType.QueryClause)) { this.CheckClassMemberRulesForQueryClause((QueryClause)child, parentExpression, parentElement, parentClass, members); } else if (child.Is(CodeUnitType.Expression)) { this.CheckClassMemberRulesForExpression((Expression)child, parentExpression, parentElement, parentClass, members); } else if (child.Is(TokenType.Literal)) { Token token = (Token)child; // Check to see whether this literal is preceded by a member access symbol. If not // then we want to check whether this is a reference to one of our class members. if (!IsLiteralTokenPrecededByMemberAccessSymbol(token)) { // Process the literal. this.CheckClassMemberRulesForLiteralToken( token, parentExpression, parentExpression, parentElement, parentClass, members); } } } }
/// <summary> /// Checks the prefix for a variable defined within a method or property. /// </summary> /// <param name="variable">The variable to check.</param> /// <param name="element">The element that contains the variable.</param> /// <param name="validPrefixes">A list of valid prefixes that should not be considered hungarian.</param> private void CheckMethodVariablePrefix( IVariable variable, Element element, Dictionary<string, string> validPrefixes) { Param.AssertNotNull(variable, "variable"); Param.AssertNotNull(element, "element"); Param.Ignore(validPrefixes); // Skip past any prefixes in the name. int index = NamingRules.MovePastPrefix(variable.VariableName); // Do not check for name casing on fields and events. These are checked elsewhere since they are elements, not simple variables, // and therefor fall under different rules. bool checkCasing = element.ElementType != ElementType.Field && element.ElementType != ElementType.Event; // Check whether the name starts with a lower-case letter. if (variable.VariableName.Length > index && char.IsLower(variable.VariableName, index)) { // Check for hungarian notation. this.CheckHungarian(variable.VariableName, index, variable.Location.LineNumber, element, validPrefixes); // Check casing on the variable. if (checkCasing && (variable.VariableModifiers & VariableModifiers.Const) == VariableModifiers.Const) { // Constants must start with an upper-case letter. this.AddViolation(element, variable.Location.LineNumber, Rules.ConstFieldNamesMustBeginWithUpperCaseLetter, variable.VariableName); } } else if (checkCasing && (variable.VariableModifiers & VariableModifiers.Const) == 0) { // Non-constant method variables must start with a lower-case letter. this.AddViolation(element, variable.Location.LineNumber, Rules.FieldNamesMustBeginWithLowerCaseLetter, variable.VariableName); } }
/// <summary> /// Checks the field name to look for underscores. /// </summary> /// <param name="field">The field ot check.</param> private void CheckFieldUnderscores(Element field) { Param.AssertNotNull(field, "field"); if (field.Name.StartsWith("s_", StringComparison.Ordinal) || field.Name.StartsWith("m_", StringComparison.Ordinal)) { this.AddViolation(field, Rules.VariableNamesMustNotBePrefixed); } else if (field.Name.StartsWith("_", StringComparison.Ordinal)) { this.AddViolation(field, Rules.FieldNamesMustNotBeginWithUnderscore); } else if (field.Name.IndexOf("_", StringComparison.Ordinal) > -1) { this.AddViolation(field, Rules.FieldNamesMustNotContainUnderscore); } }
/// <summary> /// Checks variables to look for underscores. /// </summary> /// <param name="element">The parent element.</param> /// <param name="variables">The variables to check.</param> private void CheckUnderscores(Element element, VariableCollection variables) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(variables, "variables"); foreach (IVariable variable in variables) { if (variable.VariableName.StartsWith("_", StringComparison.Ordinal) && variable.VariableName != "__arglist") { this.AddViolation(element, variable.Location.LineNumber, Rules.FieldNamesMustNotBeginWithUnderscore); } } }
/// <summary> /// Checks a variable for hungarian notation. /// </summary> /// <param name="name">The variable name.</param> /// <param name="startIndex">The index in the name where the actual name begins.</param> /// <param name="line">The number number that this variable appears on, or if 0, uses the line number /// from the element object.</param> /// <param name="element">The element that the variable appears in.</param> /// <param name="validPrefixes">A list of valid prefixes that should not be considered hungarian.</param> private void CheckHungarian( string name, int startIndex, int line, Element element, Dictionary<string, string> validPrefixes) { Param.AssertValidString(name, "name"); Param.AssertGreaterThanOrEqualToZero(startIndex, "startIndex"); Param.AssertGreaterThanZero(line, "line"); Param.AssertNotNull(element, "element"); Param.Ignore(validPrefixes); if (name.Length - startIndex > 3) { string prefix = null; for (int i = startIndex + 1; i < 3 + startIndex; ++i) { string character = name.Substring(i, 1); if (character == character.ToUpper(CultureInfo.InvariantCulture)) { prefix = name.Substring(startIndex, i - startIndex); break; } } if (prefix != null) { bool found = false; if (validPrefixes != null) { if (validPrefixes.ContainsKey(prefix)) { found = true; } } if (!found) { this.AddViolation(element, line, Rules.FieldNamesMustNotUseHungarianNotation, name); } } } }
/// <summary> /// Parses the given statement list. /// </summary> /// <param name="statementParent">The direct parent of the statements to check.</param> /// <param name="parentElement">The element that contains the statements.</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 CheckClassMemberRulesForStatements( CodeUnit statementParent, Element parentElement, ClassBase parentClass, Dictionary<string, List<Element>> members) { Param.AssertNotNull(statementParent, "statementParent"); Param.AssertNotNull(parentElement, "parentElement"); Param.Ignore(parentClass); Param.Ignore(members); // Loop through each of the statements. for (Statement statement = statementParent.FindFirstChildStatement(); statement != null; statement = statement.FindNextSiblingStatement()) { if (statement.Children.StatementCount > 0) { // Parse the sub-statements. this.CheckClassMemberRulesForStatements(statement, parentElement, parentClass, members); } // Parse the expressions in the statement. this.CheckClassMemberRulesForExpressions(statement, null, parentElement, parentClass, members); } }
/// <summary> /// Parses the list of expressions. /// </summary> /// <param name="expressionParent">The direct parent of the expressions to check.</param> /// <param name="parentExpression">The parent expression, if there is one.</param> /// <param name="parentElement">The element that contains the expressions.</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 CheckClassMemberRulesForExpressions( CodeUnit expressionParent, Expression parentExpression, Element parentElement, ClassBase parentClass, Dictionary<string, List<Element>> members) { Param.AssertNotNull(expressionParent, "expressionParent"); Param.AssertNotNull(parentElement, "parentElement"); Param.Ignore(parentExpression); Param.Ignore(parentClass); Param.Ignore(members); // Loop through each of the expressions in the list. for (Expression expression = expressionParent.FindFirstChildExpression(); expression != null; expression = expression.FindNextSiblingExpression()) { // If the expression is a variable declarator expression, we don't // want to match against the identifier tokens. if (expression.ExpressionType == ExpressionType.VariableDeclarator) { VariableDeclaratorExpression declarator = (VariableDeclaratorExpression)expression; if (declarator.Initializer != null) { this.CheckClassMemberRulesForExpression(declarator.Initializer, parentExpression, parentElement, parentClass, members); } } else { this.CheckClassMemberRulesForExpression(expression, parentExpression, parentElement, parentClass, members); } } }
private bool ProcessElement(Element element, Dictionary<string, string> validPrefixes, bool nativeMethods) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(validPrefixes, "validPrefixes"); Param.Ignore(nativeMethods); if (this.Cancel) { return false; } if (!element.Generated && element.Name != null) { switch (element.ElementType) { case ElementType.Namespace: case ElementType.Class: case ElementType.Enum: case ElementType.Struct: case ElementType.Delegate: case ElementType.Property: if (!nativeMethods) { this.CheckCase(element, element.Name, element.LineNumber, true); } break; case ElementType.Event: if (!nativeMethods) { for (EventDeclaratorExpression declarator = element.FindFirstChild<EventDeclaratorExpression>(); declarator != null; declarator = declarator.FindNextSibling<EventDeclaratorExpression>()) { this.CheckCase(element, declarator.Identifier.Text, declarator.LineNumber, true); } } break; case ElementType.Method: if (!nativeMethods && !element.Name.StartsWith("operator", StringComparison.Ordinal) && element.Name != "foreach") { this.CheckCase(element, element.Name, element.LineNumber, true); } break; case ElementType.Interface: if (element.Name.Length < 1 || element.Name[0] != 'I') { this.AddViolation(element, Rules.InterfaceNamesMustBeginWithI, element.Name); } break; case ElementType.Field: if (!nativeMethods) { this.CheckFieldUnderscores(element); this.CheckFieldPrefix(element as Field, validPrefixes); } break; default: break; } } if (!nativeMethods && (element.ElementType == ElementType.Class || element.ElementType == ElementType.Struct) && element.Name.EndsWith("NativeMethods", StringComparison.Ordinal)) { nativeMethods = true; } if (element.Children.ElementCount > 0) { for (Element child = element.FindFirstChildElement(); child != null; child = child.FindNextSiblingElement()) { if (!this.ProcessElement(child, validPrefixes, nativeMethods)) { return false; } } } if (!nativeMethods) { this.ProcessStatementContainer(element, validPrefixes); } return true; }
/// <summary> /// Parses the given expression. /// </summary> /// <param name="expression">The expression.</param> /// <param name="parentExpression">The parent expression, if there is one.</param> /// <param name="parentElement">The element that contains the expressions.</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 CheckClassMemberRulesForExpression( Expression expression, Expression parentExpression, Element parentElement, ClassBase parentClass, Dictionary<string, List<Element>> members) { Param.AssertNotNull(expression, "expression"); Param.Ignore(parentExpression); Param.AssertNotNull(parentElement, "parentElement"); Param.AssertNotNull(parentClass, "parentClass"); Param.AssertNotNull(members, "members"); if (expression.ExpressionType == ExpressionType.Literal) { LiteralExpression literalExpression = (LiteralExpression)expression; // Check to see whether this literal is preceded by a member access symbol. If not // then we want to check whether this is a reference to one of our class members. if (!IsLiteralTokenPrecededByMemberAccessSymbol(literalExpression.Token)) { // Process the literal. this.CheckClassMemberRulesForLiteralToken( literalExpression.Token, expression, parentExpression, parentElement, parentClass, members); } } else { if (expression.ExpressionType == ExpressionType.Assignment && parentExpression != null && parentExpression.ExpressionType == ExpressionType.CollectionInitializer) { // When we encounter assignment expressions within collection initializer expressions, we ignore the expression // on the left-hand side of the assignment. This is because we know that the left-hand side refers to a property on // the type being initialized, not a property on the local class. Thus, it does not ever need to be prefixed by this. // Without this check we can get name collisions, such as: // public sealed class Person // { // public string FirstName { get; } // public void CreateAnonymousType() // { // var anonymousType = new { FirstName = this.FirstName }; // } // } this.CheckClassMemberRulesForExpression(((AssignmentExpression)expression).RightHandSide, expression, parentElement, parentClass, members); } else if (expression.Children.ExpressionCount > 0) { // Check each child expression within this expression. this.CheckClassMemberRulesForExpressions(expression, expression, parentElement, parentClass, members); } if (expression.Children.QueryClauseCount > 0) { for (QueryClause clause = expression.FindFirstChildQueryClause(); clause != null; clause = clause.FindNextSiblingQueryClause()) { this.CheckClassMemberRulesForQueryClause(clause, expression, parentElement, parentClass, members); } } // Check if this is an anonymous method expression, which contains a child statement list. if (expression.ExpressionType == ExpressionType.AnonymousMethod) { // Check the statements under this anonymous method. this.CheckClassMemberRulesForStatements(expression, parentElement, parentClass, members); } else if (expression.ExpressionType == ExpressionType.MethodInvocation) { // Check each of the arguments passed into the method call. MethodInvocationExpression methodInvocation = (MethodInvocationExpression)expression; foreach (Argument argument in methodInvocation.ArgumentList) { // Check each expression within this child expression. this.CheckClassMemberRulesForExpression( argument.Expression, null, parentElement, parentClass, members); } } } }
/// <summary> /// Processes the given statement container. /// </summary> /// <param name="element">The statement container element to process.</param> /// <param name="validPrefixes">The list of acceptable Hungarian-type prefixes.</param> private void ProcessStatementContainer(Element element, Dictionary<string, string> validPrefixes) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(validPrefixes, "validPrefixes"); // Check the statement container's variables. VariableCollection variables = element.Variables; if (variables != null) { foreach (IVariable variable in variables) { if (!variable.Generated) { this.CheckMethodVariablePrefix(variable, element, validPrefixes); this.CheckUnderscores(element, variables); } } } // Check each of the statements under this container. if (element.Children.StatementCount > 0) { for (Statement statement = element.FindFirstChildStatement(); statement != null; statement = statement.FindNextSiblingStatement()) { this.ProcessStatement(statement, element, validPrefixes); } } }
/// <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); } } } } }
/// <summary> /// Processes the given statement. /// </summary> /// <param name="statement">The statement to process.</param> /// <param name="element">The parent element.</param> /// <param name="validPrefixes">The list of acceptable Hungarian-type prefixes.</param> private void ProcessStatement(Statement statement, Element element, Dictionary<string, string> validPrefixes) { Param.AssertNotNull(statement, "statement"); Param.AssertNotNull(element, "element"); Param.AssertNotNull(validPrefixes, "validPrefixes"); // Check the statement's variables. VariableCollection variables = statement.Variables; if (variables != null) { foreach (IVariable variable in variables) { this.CheckMethodVariablePrefix(variable, element, validPrefixes); this.CheckUnderscores(element, variables); } } // Check the expressions under this statement. if (statement.Children.ExpressionCount > 0) { for (Expression expression = statement.FindFirstChildExpression(); expression != null; expression = expression.FindNextSiblingExpression()) { this.ProcessExpression(expression, element, validPrefixes); } } // Check each of the statements under this statement. if (statement.Children.StatementCount > 0) { for (Statement childStatement = statement.FindFirstChildStatement(); childStatement != null; childStatement = childStatement.FindNextSiblingStatement()) { this.ProcessStatement(childStatement, element, validPrefixes); } } }
/// <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> /// Processes the given expression. /// </summary> /// <param name="expression">The expression to process.</param> /// <param name="element">The parent element.</param> /// <param name="validPrefixes">The list of acceptable Hungarian-type prefixes.</param> private void ProcessExpression(Expression expression, Element element, Dictionary<string, string> validPrefixes) { Param.AssertNotNull(expression, "expression"); Param.AssertNotNull(element, "element"); Param.AssertNotNull(validPrefixes, "validPrefixes"); // Check the type of the expression. if (expression.ExpressionType == ExpressionType.AnonymousMethod) { AnonymousMethodExpression anonymousMethod = (AnonymousMethodExpression)expression; // Check the anonymous method's variables. VariableCollection variables = anonymousMethod.Variables; if (variables != null) { foreach (IVariable variable in variables) { this.CheckMethodVariablePrefix(variable, element, validPrefixes); } // Check the statements under the anonymous method. if (anonymousMethod.Children.StatementCount > 0) { for (Statement statement = anonymousMethod.FindFirstChildStatement(); statement != null; statement = statement.FindNextSiblingStatement()) { this.ProcessStatement(statement, element, validPrefixes); } } } } // Check the child expressions under this expression. if (expression.Children.StatementCount > 0) { for (Expression childExpression = expression.FindFirstChildExpression(); childExpression != null; childExpression = childExpression.FindNextSiblingExpression()) { this.ProcessExpression(childExpression, element, validPrefixes); } } }
/// <summary> /// Checks the given expression. /// </summary> /// <param name="expression">The expression being visited.</param> /// <param name="parentElement">The parent element, if any.</param> /// <returns>Returns true to continue, or false to stop the walker.</returns> private bool VisitExpression(Expression expression, Element parentElement) { Param.AssertNotNull(expression, "expression"); Param.AssertNotNull(parentElement, "parentElement"); if (!parentElement.Generated) { if (expression.ExpressionType == ExpressionType.Query) { this.CheckQueryExpression(parentElement, (QueryExpression)expression); } else if (expression.ExpressionType == ExpressionType.MethodInvocation) { this.CheckMethodInvocationParameters(parentElement, (MethodInvocationExpression)expression); } } return true; }
/// <summary> /// Checks the case of the first character in the given word. /// </summary> /// <param name="element">The element that the word appears in.</param> /// <param name="name">The word to check.</param> /// <param name="line">The line that the word appears on.</param> /// <param name="upper">True if the character should be upper, false if it should be lower.</param> private void CheckCase(Element element, string name, int line, bool upper) { Param.AssertNotNull(element, "element"); Param.AssertValidString(name, "name"); Param.AssertGreaterThanZero(line, "line"); Param.Ignore(upper); if (name.Length >= 1) { char firstLetter = name[0]; // If the first character is not a letter, then it does not make any sense to check // for upper or lower case. if (char.IsLetter(firstLetter)) { if (upper) { if (!char.IsUpper(firstLetter)) { this.AddViolation(element, line, Rules.ElementMustBeginWithUpperCaseLetter, element.FriendlyTypeText, name); } } else { if (!char.IsLower(firstLetter)) { this.AddViolation(element, line, Rules.ElementMustBeginWithLowerCaseLetter, element.FriendlyTypeText, name); } } } } }
/// <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> /// Adds a class members to the dictionary. /// </summary> /// <param name="members">The dictionary of class members.</param> /// <param name="child">The class member.</param> /// <param name="name">The name of the class member.</param> private static void AddClassMember(Dictionary<string, List<Element>> members, Element child, string name) { Param.AssertNotNull(members, "members"); Param.AssertNotNull(child, "member"); Param.AssertValidString(name, "name"); List<Element> items = null; if (!members.TryGetValue(name, out items)) { items = new List<Element>(1); members.Add(name, items); } items.Add(child); }
/// <summary> /// Processes the given element. /// </summary> /// <param name="element">The element being visited.</param> /// <param name="settings">The settings.</param> private void CheckForRegionsInElement(Element element, Settings settings) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(settings, "settings"); // If the DoNotUseRegions setting is enabled, then skip this check as the region // will be discovered during the overall regions rule check. if (settings.DoNotPlaceRegionsWithinElements && !settings.DoNotUseRegions && !element.Generated) { if (element.ElementType == ElementType.Method || element.ElementType == ElementType.Accessor || element.ElementType == ElementType.Constructor || element.ElementType == ElementType.Destructor || element.ElementType == ElementType.Field) { for (RegionDirective region = element.FindFirstDescendent<RegionDirective>(); region != null; region = region.FindNextDescendentOf<RegionDirective>(element)) { // If this token is an opening region directive, this is a violation. if (!region.Generated && !region.IsGeneratedCodeRegion) { this.AddViolation(element, region.LineNumber, Rules.DoNotPlaceRegionsWithinElements); } } } } }
/// <summary> /// Checks the items within the given element. /// </summary> /// <param name="element">The element to check.</param> /// <param name="parentClass">The class that the element belongs to.</param> /// <param name="members">The collection of members of the parent class.</param> /// <returns>Returns false if the analyzer should quit.</returns> private bool CheckClassMemberRulesForElements(Element element, ClassBase parentClass, Dictionary<string, List<Element>> members) { Param.AssertNotNull(element, "element"); Param.Ignore(parentClass); Param.Ignore(members); // Check whether processing has been cancelled by the user. if (this.Cancel) { return false; } if (element.ElementType == ElementType.Class || element.ElementType == ElementType.Struct || element.ElementType == ElementType.Interface) { parentClass = element as ClassBase; members = CollectClassMembers(parentClass); } for (Element child = element.FindFirstChildElement(); child != null; child = child.FindNextSiblingElement()) { if (!child.Generated) { if (child.ElementType == ElementType.Method || child.ElementType == ElementType.Constructor || child.ElementType == ElementType.Destructor || child.ElementType == ElementType.Accessor) { // If the parent class is null, then this element is sitting outside of a class. // This is illegal in C# so the code will not compile, but we still attempt to // parse it. In this case there is no use of this prefixes since there is no class. if (parentClass != null) { this.CheckClassMemberRulesForStatements(child, child, parentClass, members); } } else { if (child.ElementType == ElementType.Class || child.ElementType == ElementType.Struct) { ClassBase elementContainer = child as ClassBase; Debug.Assert(elementContainer != null, "The element is not a class."); this.CheckClassMemberRulesForElements(child, elementContainer, members); } else if (!this.CheckClassMemberRulesForElements(child, parentClass, members)) { return false; } } } } return true; }