/// <summary> /// Checks the curly bracket placement on a block statement. /// </summary> /// <param name="element"> /// The element containing the statement. /// </param> /// <param name="statement"> /// The statement to check. /// </param> private void CheckBlockStatementsCurlyBracketPlacement(CsElement element, Statement statement) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(statement, "statement"); // Find the opening curly bracket. Node <CsToken> curlyBracket = GetOpeningCurlyBracketFromStatement(statement); if (curlyBracket != null) { // Find the previous token before this opening curly bracket. CsToken previousToken = GetPreviousToken(curlyBracket.Previous, statement.Tokens.MasterList); if (previousToken != null) { this.CheckTokenPrecedingOrFollowingCurlyBracket(element, previousToken); } } }
/// <summary> /// Initializes a new instance of the Delegate class. /// </summary> /// <param name="document"> /// The document that contains the element. /// </param> /// <param name="parent"> /// The parent of the element. /// </param> /// <param name="header"> /// The Xml header for this element. /// </param> /// <param name="attributes"> /// The list of attributes attached to this element. /// </param> /// <param name="declaration"> /// The declaration code for this element. /// </param> /// <param name="returnType"> /// The return type. /// </param> /// <param name="parameters"> /// The parameters to the delegate. /// </param> /// <param name="typeConstraints"> /// The list of type constraints on the element. /// </param> /// <param name="unsafeCode"> /// Indicates whether the element resides within a block of unsafe code. /// </param> /// <param name="generated"> /// Indicates whether this is generated code. /// </param> internal Delegate( CsDocument document, CsElement parent, XmlHeader header, ICollection <Attribute> attributes, Declaration declaration, TypeToken returnType, IList <Parameter> parameters, ICollection <TypeParameterConstraintClause> typeConstraints, bool unsafeCode, bool generated) : base(document, parent, ElementType.Delegate, "delegate " + declaration.Name, header, attributes, declaration, unsafeCode, generated) { Param.AssertNotNull(document, "document"); Param.AssertNotNull(parent, "parent"); Param.Ignore(header); Param.Ignore(attributes); Param.AssertNotNull(declaration, "declaration"); Param.AssertNotNull(returnType, "returnType"); Param.AssertNotNull(parameters, "parameters"); Param.Ignore(typeConstraints); Param.Ignore(unsafeCode); Param.Ignore(generated); this.returnType = returnType; this.typeConstraints = typeConstraints; this.parameters = parameters; Debug.Assert(parameters.IsReadOnly, "The parameters collection should be read-only."); // Add the qualifications this.QualifiedName = CodeParser.AddQualifications(this.parameters, this.QualifiedName); // Set the parent of the type constraint clauses. if (typeConstraints != null) { Debug.Assert(typeConstraints.IsReadOnly, "The collection of type constraints should be read-only."); foreach (TypeParameterConstraintClause constraint in typeConstraints) { constraint.ParentElement = this; } } }
/// <summary> /// Initializes a new instance of the Constructor class. /// </summary> /// <param name="document"> /// The document that contains the element. /// </param> /// <param name="parent"> /// The parent of the element. /// </param> /// <param name="header"> /// The Xml header for this element. /// </param> /// <param name="attributes"> /// The list of attributes attached to this element. /// </param> /// <param name="declaration"> /// The declaration code for this element. /// </param> /// <param name="parameters"> /// The parameters to the constructor. /// </param> /// <param name="initializerExpression"> /// The constructor initializer, if there is one. /// </param> /// <param name="unsafeCode"> /// Indicates whether the element resides within a block of unsafe code. /// </param> /// <param name="generated"> /// Indicates whether the code element was generated or written by hand. /// </param> internal Constructor( CsDocument document, CsElement parent, XmlHeader header, ICollection <Attribute> attributes, Declaration declaration, IList <Parameter> parameters, MethodInvocationExpression initializerExpression, bool unsafeCode, bool generated) : base(document, parent, ElementType.Constructor, "constructor " + declaration.Name, header, attributes, declaration, unsafeCode, generated) { Param.AssertNotNull(document, "document"); Param.AssertNotNull(parent, "parent"); Param.Ignore(header); Param.Ignore(attributes); Param.AssertNotNull(declaration, "declaration"); Param.AssertNotNull(parameters, "parameters"); Param.Ignore(initializerExpression); Param.Ignore(unsafeCode); Param.Ignore(generated); // Static constructors are treated as private and handled as a special case for ordering. if (this.Declaration.ContainsModifier(CsTokenType.Static)) { this.Declaration.AccessModifierType = AccessModifierType.Private; } this.parameters = parameters; Debug.Assert(parameters.IsReadOnly, "The parameters collection should be read-only."); // Add the qualifications this.QualifiedName = CodeParser.AddQualifications(this.parameters, this.QualifiedName); // If there is an initializer expression, add it to the statement list for this constructor. if (initializerExpression != null) { this.initializer = initializerExpression; ConstructorInitializerStatement initializerStatement = new ConstructorInitializerStatement(initializerExpression.Tokens, initializerExpression); this.AddStatement(initializerStatement); } }
/// <summary> /// Returns True if the text has incorrect spelling. /// </summary> /// <param name="element"> /// The element containing the text we're checking. /// </param> /// <param name="text"> /// The text to check. /// </param> /// <param name="spellingError"> /// Returns a comma separated list of words encountered as spelling errors. /// </param> /// <returns> /// True if the text contains an incorrect spelling. /// </returns> private static bool TextContainsIncorectSpelling(CsElement element, string text, out string spellingError) { NamingService namingService = NamingService.GetNamingService(element.Document.SourceCode.Project.Culture); spellingError = string.Empty; if (namingService.SupportsSpelling) { ICollection <string> recognizedWords = element.Document.SourceCode.Project.RecognizedWords; WordParser parser = new WordParser(text, WordParserOptions.SplitCompoundWords, recognizedWords); if (parser.PeekWord() != null) { string word = parser.NextWord(); do { // Ignore words starting and ending with '$'. // Ignore if in our recognized words list or correct spelling string hint = null; if ((word.StartsWith("$") && word.EndsWith("$")) || (recognizedWords.Contains(word) || IsSpelledCorrectly(namingService, word, out hint))) { continue; } // If we already have a spelling error for this element, add a comma to separate them. if (!string.IsNullOrEmpty(spellingError)) { spellingError += ", "; } spellingError += "'" + word + "'"; // Append a hint message to this spelling error if we have one. if (hint != null && hint.Trim().Length > 0) { spellingError += " " + hint; } }while ((word = parser.NextWord()) != null); } } return(!string.IsNullOrEmpty(spellingError)); }
/// <summary> /// Adds a rule suppression for the given element. /// </summary> /// <param name="element"> /// The element. /// </param> /// <param name="ruleId"> /// The ID of the rule to suppress. /// </param> /// <param name="ruleName"> /// The name of the rule. /// </param> /// <param name="ruleNamespace"> /// The namespace in which the rule is contained. /// </param> internal void AddRuleSuppression(CsElement element, string ruleId, string ruleName, string ruleNamespace) { Param.AssertNotNull(element, "element"); Param.AssertValidString(ruleId, "ruleId"); Param.Assert(ruleId == "*" || !string.IsNullOrEmpty(ruleName), "ruleName", "Rule name is invalid."); Param.AssertValidString(ruleNamespace, "ruleNamespace"); SuppressedRule suppressedRule = new SuppressedRule { RuleId = ruleId, RuleName = ruleName, RuleNamespace = ruleNamespace }; // Need a writer lock arond this entire section to ensure thread safety of dictionary // and the lists contained inside. this.suppressionsLock.AcquireWriterLock(Timeout.Infinite); try { // Determine whether there is already at least one suppression for some element for this rule. List <CsElement> elementsContainingSuppressedRule = null; bool foundElementList = false; if (this.suppressions.Count != 0) { foundElementList = this.suppressions.TryGetValue(suppressedRule, out elementsContainingSuppressedRule); } Debug.Assert( !foundElementList || elementsContainingSuppressedRule != null, "The returned list of elements containing the suppressed rule should never be null."); if (!foundElementList) { // This is the first suppression for this rule type. elementsContainingSuppressedRule = new List <CsElement>(); this.suppressions.Add(suppressedRule, elementsContainingSuppressedRule); } elementsContainingSuppressedRule.Add(element); } finally { this.suppressionsLock.ReleaseWriterLock(); } }
private bool WalkMethods(CsElement element, CsElement parentElement, object context) { if (element.ElementType != ElementType.Method) { return(true); } var method = (Method)element; foreach (MethodVisitorInfo methodVisitor in this.methodVisitors) { foreach (MethodViolationData violationData in methodVisitor.MethodVisitor.Visit(method)) { this.AddViolation(method, method.Location, methodVisitor.RuleName, violationData.Parameters); } } return(true); }
/// <summary> /// Checks the given list of expressions. /// </summary> /// <param name="element"> /// The element containing the expressions. /// </param> /// <param name="expressions"> /// The list of expressions. /// </param> private void CheckStatementFormattingRulesForExpressions(CsElement element, ICollection <Expression> expressions) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(expressions, "expressions"); foreach (Expression expression in expressions) { if (expression.ExpressionType == ExpressionType.AnonymousMethod) { // Check the statements within this anonymous method expression. AnonymousMethodExpression anonymousMethod = expression as AnonymousMethodExpression; this.CheckStatementFormattingRulesForStatements(element, anonymousMethod.ChildStatements); } else { // Check the child expressions under this expression. this.CheckStatementFormattingRulesForExpressions(element, expression.ChildExpressions); } } }
/// <summary> /// Finds the ClassBase object above this element representing a Class or Struct. /// </summary> /// <param name="element"> /// The element to start at. /// </param> /// <returns> /// The ClassBase for the element. /// </returns> public static ClassBase GetClassBase(CsElement element) { bool foundParentClass = false; ICodePart parent = element.Parent; while (!foundParentClass && parent != null) { if (parent is ClassBase) { foundParentClass = true; } else { parent = parent.Parent; } } return(parent as ClassBase); }
/// <summary> /// Checks the argument list to a method or method invocation to ensure that the arguments are /// positioned correctly. /// </summary> /// <param name="element"> /// The element containing the expression. /// </param> /// <param name="arguments"> /// The arguments to the method. /// </param> /// <param name="openingBracketNode"> /// The opening bracket token. /// </param> /// <param name="methodLineNumber"> /// The line number on which the method begins. /// </param> /// <param name="friendlyTypeText"> /// The text to use for the type in reporting violations. /// </param> private void CheckMethodArgumentList(CsElement element, IArgumentList arguments, Node <CsToken> openingBracketNode, int methodLineNumber, string friendlyTypeText) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(arguments, "arguments"); Param.AssertNotNull(openingBracketNode, "openingBracketNode"); Param.AssertGreaterThanZero(methodLineNumber, "methodLineNumber"); // Determine whether all of the parameters are on the same line as one another. bool someParametersShareLine; bool someParameterOnDifferentLines; DetermineMethodParameterPlacementScheme(arguments, out someParametersShareLine, out someParameterOnDifferentLines); // All parameters must either be on the same line, or each parameter must begin on its own line. if (someParametersShareLine && someParameterOnDifferentLines) { this.AddViolation(element, methodLineNumber, Rules.ParametersMustBeOnSameLineOrSeparateLines, friendlyTypeText); } // Determine whether all of the parameters are on the same line as one another. if (someParameterOnDifferentLines) { this.CheckSplitMethodArgumentList(element, arguments, openingBracketNode, friendlyTypeText); } else if (arguments.Count > 0) { // The first argument must start on the same line as the opening bracket, or // on the line after it. int firstArgumentStartLine = arguments.Location(0).LineNumber; if (firstArgumentStartLine != openingBracketNode.Value.LineNumber && firstArgumentStartLine != openingBracketNode.Value.LineNumber + 1) { int commentLineSpan = MeasureCommentLinesAfter(openingBracketNode); if (firstArgumentStartLine != openingBracketNode.Value.LineNumber + commentLineSpan + 1) { this.AddViolation(element, firstArgumentStartLine, Rules.ParameterListMustFollowDeclaration); } } } }
/// <summary> /// Checks the placement of statements within the given element. /// </summary> /// <param name="element"> /// The element to check. /// </param> private void CheckStatementFormattingRulesForElement(CsElement element) { Param.AssertNotNull(element, "element"); if (!element.Generated) { if (element.ElementType == ElementType.EmptyElement) { this.AddViolation(element, element.LineNumber, Rules.CodeMustNotContainEmptyStatements); } else { this.CheckStatementFormattingRulesForStatements(element, element.ChildStatements); foreach (CsElement child in element.ChildElements) { this.CheckStatementFormattingRulesForElement(child); } } } }
/// <summary> /// Checks the order of elements that appear within the given element. /// </summary> /// <param name="element"> /// The element to check. /// </param> /// <param name="checkGeneratedCode"> /// True to check the element order of generated code blocks. /// </param> private void CheckElementOrder(CsElement element, bool checkGeneratedCode) { Param.AssertNotNull(element, "element"); Param.Ignore(checkGeneratedCode); // Check the ordering of the keywords in the element's declaration. if (!element.Generated && (element.ElementType == ElementType.Class || element.ElementType == ElementType.Field || element.ElementType == ElementType.Enum || element.ElementType == ElementType.Struct || element.ElementType == ElementType.Interface || element.ElementType == ElementType.Delegate || element.ElementType == ElementType.Event || element.ElementType == ElementType.Property || element.ElementType == ElementType.Indexer || element.ElementType == ElementType.Method || element.ElementType == ElementType.Constructor || element.ElementType == ElementType.Accessor)) { this.CheckDeclarationKeywordOrder(element); } // Make sure that using directives are inside of namespace elements. this.CheckUsingDirectivePlacement(element); // Checks the order of the children of this element. this.CheckChildElementOrdering(element, checkGeneratedCode); }
/// <summary> /// Parses the given statement list. /// </summary> /// <param name="statements"> /// The list of statements to parse. /// </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( ICollection <Statement> statements, CsElement parentElement, ClassBase parentClass, Dictionary <string, List <CsElement> > members) { Param.AssertNotNull(statements, "statements"); Param.AssertNotNull(parentElement, "parentElement"); Param.Ignore(parentClass); Param.Ignore(members); // Loop through each of the statements. foreach (Statement statement in statements) { if (statement.ChildStatements.Count > 0) { // Parse the sub-statements. this.CheckClassMemberRulesForStatements(statement.ChildStatements, parentElement, parentClass, members); } // Parse the expressions in the statement. this.CheckClassMemberRulesForExpressions(statement.ChildExpressions, null, parentElement, parentClass, members); } }
/// <summary> /// Initializes a new instance of the <see cref="CodeWalker{T}"/> class. /// Initializes a new instance of the CodeWalker class. /// </summary> /// <param name="element"> /// The element to walk through. /// </param> /// <param name="elementCallback"> /// Callback executed when an element is visited. /// </param> /// <param name="statementCallback"> /// Callback executed when a statement is visited. /// </param> /// <param name="expressionCallback"> /// Callback executed when an expression is visited. /// </param> /// <param name="queryClauseCallback"> /// Callback executed when a query clause is visited. /// </param> /// <param name="context"> /// The optional visitor context data. /// </param> private CodeWalker( CsElement element, CodeWalkerElementVisitor <T> elementCallback, CodeWalkerStatementVisitor <T> statementCallback, CodeWalkerExpressionVisitor <T> expressionCallback, CodeWalkerQueryClauseVisitor <T> queryClauseCallback, T context) { Param.AssertNotNull(element, "element"); Param.Ignore(elementCallback); Param.Ignore(statementCallback); Param.Ignore(expressionCallback); Param.Ignore(queryClauseCallback); Param.Ignore(context); this.elementCallback = elementCallback; this.statementCallback = statementCallback; this.expressionCallback = expressionCallback; this.queryClauseCallback = queryClauseCallback; this.WalkElement(element, element.FindParentElement(), context); }
/// <summary> /// Fills in the details of the get accessor. /// </summary> /// <param name="parent"> /// The parent of the accessor. /// </param> private void FillGetAccessorDetails(CsElement parent) { Param.AssertNotNull(parent, "parent"); Property property = parent as Property; if (property != null) { // Get accessors on properties have the return type of their parent property, // and have no input parameters. this.returnType = property.ReturnType; } else { // Get accessors on indexers have the return type of their parent indexer, // and have the input parameters of the parent indexer. Indexer indexer = (Indexer)parent; this.returnType = indexer.ReturnType; this.parameters = indexer.Parameters; } }
/// <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, CsElement 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) { if (char.IsUpper(name, i)) { 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); } } } }
private bool IsSpecialCaseExclusion(CsElement first, CsElement second) { //// Note: While many checks here are redundant/unnecessary, they are explictly specified to //// avoid any ambiguity. // Special case for static constructors, which are always private but should still appear in front of all other constructors. if (first.ElementType == ElementType.Constructor && second.ElementType == ElementType.Constructor && first.Declaration.ContainsModifier(CsTokenType.Static) && !second.Declaration.ContainsModifier(CsTokenType.Static)) { return(true); } // Special case for readonly dependency property pattern. if (first.ElementType == ElementType.Field && second.ElementType == ElementType.Field && first.Declaration.ContainsModifier(CsTokenType.Static) && second.Declaration.ContainsModifier(CsTokenType.Static) && first.AccessModifier == AccessModifierType.Private && second.AccessModifier == AccessModifierType.Public && first.Declaration.Tokens.Any(t => t.CsTokenClass == CsTokenClass.Type && t.Text == "DependencyPropertyKey")) { return(true); } return(false); }
/// <summary> /// Processes the given element. /// </summary> /// <param name="element"> /// The element being visited. /// </param> private void CheckMethodParameters(CsElement element) { Param.AssertNotNull(element, "element"); IList <Parameter> parameters = null; CsTokenType openBracketType = CsTokenType.OpenParenthesis; CsTokenType closeBracketType = CsTokenType.CloseParenthesis; if (element.ElementType == ElementType.Constructor) { parameters = ((Constructor)element).Parameters; } else if (element.ElementType == ElementType.Delegate) { parameters = ((Delegate)element).Parameters; } else if (element.ElementType == ElementType.Method) { parameters = ((Method)element).Parameters; } else if (element.ElementType == ElementType.Indexer) { parameters = ((Indexer)element).Parameters; openBracketType = CsTokenType.OpenSquareBracket; closeBracketType = CsTokenType.CloseSquareBracket; } if (parameters != null) { ParameterList parameterList = new ParameterList(parameters); CsTokenList parameterListTokens = GetParameterListTokens(element.Declaration.Tokens, openBracketType, closeBracketType); if (parameterListTokens != null) { this.CheckParameters(element, parameterListTokens, parameterList, element.LineNumber, openBracketType, closeBracketType, element.FriendlyTypeText); } } }
/// <summary> /// Initializes a new instance of the Indexer class. /// </summary> /// <param name="document"> /// The document that contains the element. /// </param> /// <param name="parent"> /// The parent of the element. /// </param> /// <param name="header"> /// The Xml header for this element. /// </param> /// <param name="attributes"> /// The list of attributes attached to this element. /// </param> /// <param name="declaration"> /// The declaration code for this element. /// </param> /// <param name="returnType"> /// The return type of the indexer. /// </param> /// <param name="parameters"> /// The parameters to the indexer. /// </param> /// <param name="unsafeCode"> /// Indicates whether the element resides within a block of unsafe code. /// </param> /// <param name="generated"> /// Indicates whether the code element was generated or written by hand. /// </param> internal Indexer( CsDocument document, CsElement parent, XmlHeader header, ICollection <Attribute> attributes, Declaration declaration, TypeToken returnType, IList <Parameter> parameters, bool unsafeCode, bool generated) : base(document, parent, ElementType.Indexer, "indexer " + declaration.Name, header, attributes, declaration, unsafeCode, generated) { Param.AssertNotNull(document, "document"); Param.AssertNotNull(parent, "parent"); Param.Ignore(header); Param.Ignore(attributes); Param.AssertNotNull(declaration, "declaration"); Param.Ignore(returnType); Param.AssertNotNull(parameters, "parameters"); Param.Ignore(unsafeCode); Param.Ignore(generated); this.returnType = returnType; this.parameters = parameters; Debug.Assert(parameters.IsReadOnly, "The parameters collection should be read-only."); // Add the qualifications this.QualifiedName = CodeParser.AddQualifications(this.parameters, this.QualifiedName); // If this is an explicit interface member implementation and our access modifier // is currently set to private because we don't have one, then it should be public instead. if (this.Declaration.Name.IndexOf(".", StringComparison.Ordinal) > -1 && !this.Declaration.Name.StartsWith("this.", StringComparison.Ordinal)) { this.Declaration.AccessModifierType = AccessModifierType.Public; } }
/// <summary> /// Fills in the details of the accessor based on its type. /// </summary> /// <param name="parent"> /// The parent of the accessor. /// </param> private void FillDetails(CsElement parent) { Param.AssertNotNull(parent, "parent"); // Set the return type and parameters. if (this.accessorType == AccessorType.Get) { this.FillGetAccessorDetails(parent); } else if (this.accessorType == AccessorType.Set) { this.FillSetAccessorDetails(parent); } else { // Add and remove accessors have no return type but have an implied // parameter based on the type of the event handler. Event parentEvent = (Event)parent; Reference <ICodePart> accessorReference = new Reference <ICodePart>(this); this.returnType = CreateVoidTypeToken(accessorReference); this.parameters = new[] { new Parameter( parentEvent.EventHandlerType, "value", accessorReference, ParameterModifiers.None, null, CodeLocation.Empty, null, parentEvent.EventHandlerType.Generated) }; } }
/// <summary> /// Checks a method invocation expression to make that the parameters are positioned correctly. /// </summary> /// <param name="element"> /// The element containing the expression. /// </param> /// <param name="expression"> /// The expression to check. /// </param> private void CheckMethodInvocationParameters(CsElement element, MethodInvocationExpression expression) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(expression, "expression"); if (expression.Tokens.First != null && !expression.Tokens.First.Value.Generated) { ArgumentList argumentList = new ArgumentList(expression.Arguments); CsTokenList argumentListTokens = GetArgumentListTokens( expression.Tokens, expression.Name.Tokens.Last, CsTokenType.OpenParenthesis, CsTokenType.CloseParenthesis); if (argumentListTokens != null) { this.CheckParameters( element, argumentListTokens, argumentList, expression.LineNumber, CsTokenType.OpenParenthesis, CsTokenType.CloseParenthesis, element.FriendlyTypeText); } } }
/// <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(CsElement element, Dictionary <string, string> validPrefixes) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(validPrefixes, "validPrefixes"); // Check the statement container's variables. if (element.Variables != null) { foreach (Variable variable in element.Variables) { if (!variable.Generated) { this.CheckMethodVariablePrefix(variable, element, validPrefixes); this.CheckUnderscores(element, element.Variables); } } } // Check each of the statements under this container. foreach (Statement statement in element.ChildStatements) { this.ProcessStatement(statement, element, validPrefixes); } }
/// <summary> /// Checks the order of child elements of the given element. /// </summary> /// <param name="element"> /// The element to check. /// </param> /// <param name="checkGeneratedCode"> /// Indicates whether to check the order of elements /// within generated blocks of code. /// </param> private void CheckChildElementOrdering(CsElement element, bool checkGeneratedCode) { Param.AssertNotNull(element, "element"); Param.Ignore(checkGeneratedCode); // Check the ordering of this element compared with other elements at the same level. if (element.ChildElements.Count > 0) { bool firstValid = true; CsElement[] elementsArray = new CsElement[element.ChildElements.Count]; element.ChildElements.CopyTo(elementsArray, 0); for (int i = 0; i < elementsArray.Length; ++i) { CsElement first = elementsArray[i]; if (first.AnalyzerTag == null) { for (int j = i + 1; j < elementsArray.Length; ++j) { CsElement second = elementsArray[j]; if (second.AnalyzerTag == null) { // If we're supposed to be checking the order of generated code as well, // then only perform this check if at least one of the two elements is not // generated code. Otherwise, only perform this check if both of the two // elements is not generated code. if ((checkGeneratedCode && (!first.Generated || !second.Generated)) || (!checkGeneratedCode && !first.Generated && !second.Generated)) { // Determine whether first is actually supposed to come before second if (!this.CompareItems(first, second, !firstValid)) { // Determine whether this means that first is out of order or second // is out of order. If we have not found the first item in the list that // is in the correct order, then first is marked out of order, otherwise // second is marked out of order. if (firstValid) { first.AnalyzerTag = false; } else { second.AnalyzerTag = false; } } else { // At this point we know that we've found an item that is in the correct order. if (firstValid) { firstValid = false; } } // If both of the elements are accessors, check that they appear in the correct order. if (first.ElementType == ElementType.Accessor && second.ElementType == ElementType.Accessor) { Accessor firstAccessor = (Accessor)first; Accessor secondAccessor = (Accessor)second; if (firstAccessor.AccessorType == AccessorType.Set && secondAccessor.AccessorType == AccessorType.Get) { this.AddViolation(first, Rules.PropertyAccessorsMustFollowOrder); } else if (firstAccessor.AccessorType == AccessorType.Remove && secondAccessor.AccessorType == AccessorType.Add) { this.AddViolation(first, Rules.EventAccessorsMustFollowOrder); } } } } } } this.CheckElementOrder(first, checkGeneratedCode); } } }
private void CheckForEmptyComments(DocumentRoot element) { Param.AssertNotNull(element, "element"); // Loop through all the tokens in the file looking for comments. for (Node <CsToken> tokenNode = element.Tokens.First; !element.Tokens.OutOfBounds(tokenNode); tokenNode = tokenNode.Next) { if (this.Cancel) { break; } CsToken token = tokenNode.Value; // Skip generated code. if (!token.Generated) { // Check for the two comment types. if (token.CsTokenType == CsTokenType.SingleLineComment) { // 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 < token.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 (token.Text[character] != ' ' && token.Text[character] != '\t' && token.Text[character] != '\r' && token.Text[character] != '\n') { found = true; break; } } else if (token.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 comment = false; int lines = 0; foreach (CsToken item in element.Tokens.ReverseIterator(tokenNode.Previous)) { if (item.Text == "\n") { ++lines; if (lines > 1) { break; } } else if (item.CsTokenType == CsTokenType.SingleLineComment) { comment = true; break; } else if (item.CsTokenType != CsTokenType.WhiteSpace) { break; } } if (!comment) { CsElement errorElement = token.Parent as CsElement ?? element; this.AddViolation(errorElement, token.Location, Rules.CommentsMustContainText); } else { comment = false; lines = 0; foreach (CsToken item in element.Tokens.ForwardIterator(tokenNode.Next)) { if (item.Text == "\n") { ++lines; if (lines > 1) { break; } } else if (item.CsTokenType == CsTokenType.SingleLineComment) { comment = true; break; } else if (item.CsTokenType != CsTokenType.WhiteSpace) { break; } } if (!comment) { CsElement errorElement = token.Parent as CsElement ?? element; this.AddViolation(errorElement, token.Location, Rules.CommentsMustContainText); } } } } else if (token.CsTokenType == CsTokenType.MultiLineComment) { // The is a multi-line comment. Get the start of the comment. int start = token.Text.IndexOf("/*", StringComparison.Ordinal); if (start > -1) { // Get the end of the comment int end = token.Text.IndexOf("*/", start + 2, StringComparison.Ordinal); if (end > -1) { // Look at the characters between the start and the end and see if there // is anything here besides whitespace. bool found = false; for (int character = start + 2; character < end; ++character) { if (token.Text[character] != ' ' && token.Text[character] != '\t' && token.Text[character] != '\r' && token.Text[character] != '\n') { found = true; break; } } // Check whether the comment contained any text. if (!found) { CsElement errorElement = token.Parent as CsElement ?? element; this.AddViolation(errorElement, token.Location, Rules.CommentsMustContainText); } } } } } } }
/// <summary> /// Initializes a new instance of the Method class. /// </summary> /// <param name="document"> /// The document that contains the element. /// </param> /// <param name="parent"> /// The parent of the element. /// </param> /// <param name="header"> /// The Xml header for this element. /// </param> /// <param name="attributes"> /// The list of attributes attached to this element. /// </param> /// <param name="declaration"> /// The declaration code for this element. /// </param> /// <param name="returnType"> /// The method's return type. /// </param> /// <param name="parameters"> /// The parameters to the method. /// </param> /// <param name="typeConstraints"> /// The list of type constraints on the element. /// </param> /// <param name="unsafeCode"> /// Indicates whether the element resides within a block of unsafe code. /// </param> /// <param name="generated"> /// Indicates whether the code element was generated or written by hand. /// </param> internal Method( CsDocument document, CsElement parent, XmlHeader header, ICollection <Attribute> attributes, Declaration declaration, TypeToken returnType, IList <Parameter> parameters, ICollection <TypeParameterConstraintClause> typeConstraints, bool unsafeCode, bool generated) : base(document, parent, ElementType.Method, "method " + declaration.Name, header, attributes, declaration, unsafeCode, generated) { Param.AssertNotNull(document, "document"); Param.AssertNotNull(parent, "parent"); Param.Ignore(header); Param.Ignore(attributes); Param.AssertNotNull(declaration, "declaration"); Param.Ignore(returnType); Param.AssertNotNull(parameters, "parameters"); Param.Ignore(typeConstraints); Param.Ignore(unsafeCode); Param.Ignore(generated); Debug.Assert( returnType != null || declaration.ContainsModifier(CsTokenType.Explicit, CsTokenType.Implicit), "A method's return type can only be null in an explicit or implicit operator overload method."); this.returnType = returnType; this.parameters = parameters; this.typeConstraints = typeConstraints; Debug.Assert(parameters.IsReadOnly, "The parameters collection should be read-only."); // Determine whether this is an extension method. The method must be static. if (this.parameters.Count > 0 && this.Declaration.ContainsModifier(CsTokenType.Static)) { // Look at this first parameter. Since the parameters collection is not an indexable list, the // easiest way to do this is to foreach through the parameter list and break after the first one. foreach (Parameter parameter in this.parameters) { if ((parameter.Modifiers & ParameterModifiers.This) != 0) { this.extensionMethod = true; } break; } } // Add the qualifications. this.QualifiedName = CodeParser.AddQualifications(this.parameters, this.QualifiedName); // If this is an explicit interface member implementation and our access modifier // is currently set to private because we don't have one, then it should be public instead. if (this.Declaration.Name.IndexOf(".", StringComparison.Ordinal) > -1 && !this.Declaration.Name.StartsWith("this.", StringComparison.Ordinal)) { this.Declaration.AccessModifierType = AccessModifierType.Public; } // Set the parent of the type constraint clauses. if (typeConstraints != null) { foreach (TypeParameterConstraintClause constraint in typeConstraints) { constraint.ParentElement = this; } } }
/// <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, CsElement parentElement, ClassBase parentClass, Dictionary <string, List <CsElement> > 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.TokenNode, expression.Tokens.MasterList)) { // Process the literal. this.CheckClassMemberRulesForLiteralToken(literalExpression.TokenNode, 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.ChildExpressions.Count > 0) { // Check each child expression within this expression. this.CheckClassMemberRulesForExpressions(expression.ChildExpressions, 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.ChildStatements, 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.Arguments) { // Check each expression within this child expression. if (argument.Expression.ExpressionType != ExpressionType.MethodInvocation) { this.CheckClassMemberRulesForExpression(argument.Expression, null, parentElement, parentClass, members); } } } } }
/// <summary> /// Analyzes the given query clauses. /// </summary> /// <param name="element"> /// The element containing the clauses. /// </param> /// <param name="expression"> /// The expression containing the clauses. /// </param> /// <param name="clauses"> /// The list of clauses to analyze. /// </param> /// <param name="previousClause"> /// The previous clause in the expression, if any. /// </param> /// <param name="clauseOnSameLine"> /// Indicates whether any clause has been seen previously which /// starts on the same line as the clause before it. /// </param> /// <param name="clauseOnSeparateLine"> /// Indicates whether any clause has been seen previously which /// starts on the line after the clause before it. /// </param> /// <returns> /// Returns true to continue checking the query clause, or false to quit. /// </returns> private bool ProcessQueryClauses( CsElement element, QueryExpression expression, ICollection <QueryClause> clauses, ref QueryClause previousClause, ref bool clauseOnSameLine, ref bool clauseOnSeparateLine) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(expression, "expression"); Param.AssertNotNull(clauses, "clauses"); Param.Ignore(previousClause); Param.Ignore(clauseOnSameLine); Param.Ignore(clauseOnSeparateLine); foreach (QueryClause clause in clauses) { if (previousClause != null) { // Figure out the line number that the previous clause ends on. For most // clauses, this is simply the end point of the clause location property, // but for continuation clauses we want to use the location of the 'into' variable, // which conceptually represents the end of the continuation line. int previousClauseEndLineNumber = previousClause.Location.EndPoint.LineNumber; if (previousClause.QueryClauseType == QueryClauseType.Continuation) { previousClauseEndLineNumber = ((QueryContinuationClause)previousClause).Variable.Location.LineNumber; } // Ensure that the clause either starts on the same line as the expression, or // on the very next line. if (clause.LineNumber == previousClauseEndLineNumber) { // This is only ok if the previous clause does not span multiple lines. if (previousClause.Location.LineSpan > 1) { this.AddViolation(element, clause.LineNumber, Rules.QueryClauseMustBeginOnNewLineWhenPreviousClauseSpansMultipleLines); return(false); } // The rest of the checks are only applied when the clause is not a query continuation clause. A continuation // clause is allowed to begin at the end of the previous clause, on the same line. if (clause.QueryClauseType != QueryClauseType.Continuation) { // The clause starts on the same line as the ending of the previous clause. // This is ok as long as we have not previously seen a clause which starts // on its own line. The one exception is that query continuation clauses // are allowed to be inserted at the end of the previous claus. if (clauseOnSeparateLine) { this.AddViolation(element, clause.LineNumber, Rules.QueryClausesMustBeOnSeparateLinesOrAllOnOneLine); return(false); } // If the clause spans multiple lines, it must begin on its own line. The exception is query continuation // clauses, which are allowed to begin at the end of the previous claus. if (clause.Location.LineSpan > 1) { this.AddViolation(element, clause.LineNumber, Rules.QueryClausesSpanningMultipleLinesMustBeginOnOwnLine); return(false); } // Indicate that we have seen a clause which starts on the same line as the // previous clause. clauseOnSameLine = true; } } else if (clause.LineNumber == previousClauseEndLineNumber + 1) { // The clause starts on the line just after the previous clause. // This is fine unless we have previously seen two clauses on the same line. if (clauseOnSameLine) { this.AddViolation(element, clause.LineNumber, Rules.QueryClausesMustBeOnSeparateLinesOrAllOnOneLine); return(false); } // Indicate that we have seen a clause which begins on the line after // the previous clause. clauseOnSeparateLine = true; } else if (clause.LineNumber > previousClauseEndLineNumber + 1) { // The clause does not start on the line after the previous clause. this.AddViolation(element, clause.LineNumber, Rules.QueryClauseMustFollowPreviousClause); return(false); } } previousClause = clause; if (clause.QueryClauseType == QueryClauseType.Continuation) { QueryContinuationClause continuationClause = (QueryContinuationClause)clause; if ( !this.ProcessQueryClauses( element, expression, continuationClause.ChildClauses, ref previousClause, ref clauseOnSameLine, ref clauseOnSeparateLine)) { return(false); } } } return(true); }
/// <summary> /// Initializes a new instance of the Namespace class. /// </summary> /// <param name="document"> /// The document that contains the element. /// </param> /// <param name="parent"> /// The parent of the element. /// </param> /// <param name="header"> /// The Xml header for this element. /// </param> /// <param name="attributes"> /// The list of attributes attached to this element. /// </param> /// <param name="declaration"> /// The declaration code for this element. /// </param> /// <param name="unsafeCode"> /// Indicates whether the element resides within a block of unsafe code. /// </param> /// <param name="generated"> /// Indicates whether the code element was generated or written by hand. /// </param> internal Namespace( CsDocument document, CsElement parent, XmlHeader header, ICollection <Attribute> attributes, Declaration declaration, bool unsafeCode, bool generated) : base(document, parent, ElementType.Namespace, "namespace " + declaration.Name, header, attributes, declaration, unsafeCode, generated) { Param.Ignore(document, parent, header, attributes, declaration, unsafeCode, generated); }
/// <summary> /// Initializes a new instance of the CsElement class. /// </summary> /// <param name="document"> /// The document that contains the element. /// </param> /// <param name="parent"> /// The parent of the element. /// </param> /// <param name="type"> /// The element type. /// </param> /// <param name="name"> /// The name of this element. /// </param> /// <param name="header"> /// The Xml header for this element. /// </param> /// <param name="attributes"> /// The list of attributes attached to this element. /// </param> /// <param name="declaration"> /// The declaration code for this element. /// </param> /// <param name="unsafeCode"> /// Indicates whether the element is unsafe. /// </param> /// <param name="generated"> /// Indicates whether the element was generated or written by hand. /// </param> internal CsElement( CsDocument document, CsElement parent, ElementType type, string name, XmlHeader header, ICollection <Attribute> attributes, Declaration declaration, bool unsafeCode, bool generated) : base(CodePartType.Element) { Param.AssertNotNull(document, "document"); Param.Ignore(parent); Param.Ignore(type); Param.AssertNotNull(name, "name"); Param.Ignore(header); Param.Ignore(attributes); Param.Ignore(declaration); Param.Ignore(unsafeCode); Param.Ignore(generated); this.document = document; if (this.document == null) { throw new ArgumentException(Strings.DocumentMustBeCsDocument, "document"); } if (parent != null && parent.Document != document) { throw new ArgumentException(Strings.ElementMustBeInParentsDocument, "parent"); } this.type = type; this.name = CodeLexer.DecodeEscapedText(name, true); this.header = header; this.attributes = attributes; this.declaration = declaration; this.unsafeCode = unsafeCode; this.generated = generated; if (!unsafeCode && this.declaration.ContainsModifier(CsTokenType.Unsafe)) { this.unsafeCode = true; } // Fill in the element reference in the header object. if (this.header != null) { Debug.Assert(this.header.Element == null, "The header element should not be empty."); this.header.Element = this; } // Fill in the element reference in the attributes list items. if (this.attributes != null) { Debug.Assert(this.attributes.IsReadOnly, "The attributes collection should be read-only."); foreach (Attribute attribute in this.attributes) { Debug.Assert(attribute.Element == null, "The attribute element should not be empty"); attribute.Element = this; } } // Set the fully qualified base name if (parent != null) { this.fullyQualifiedBase = parent.FullyQualifiedName; this.MergeAccess(parent.ActualAccess); } else { if (this.declaration != null) { this.actualAccess = this.declaration.AccessModifierType; } else { this.actualAccess = AccessModifierType.Public; } } // Set the fully qualified name this.fullyQualifiedName = this.fullyQualifiedBase; if (this.declaration != null && this.declaration.Name != null && this.declaration.Name.Length > 0) { if (this.fullyQualifiedBase != null && this.fullyQualifiedBase.Length > 0) { this.fullyQualifiedName += "."; } int index = this.declaration.Name.LastIndexOf("\\", StringComparison.Ordinal); if (index != -1) { this.fullyQualifiedName += this.declaration.Name.Substring(index + 1, this.declaration.Name.Length - index - 1); } else { this.fullyQualifiedName += this.declaration.Name; } index = this.fullyQualifiedName.IndexOf(".cs.", StringComparison.OrdinalIgnoreCase); if (-1 == index) { this.fullNamespaceName = this.fullyQualifiedName; } else { this.fullNamespaceName = this.fullyQualifiedName.Substring(index + 4, this.fullyQualifiedName.Length - index - 4); } } // There is only one type of element which is allowed to have a token // list consisting of nothing other than whitespace, newlines, etc., // which is the document root. This happens if you have a document which // contains nothing other than whitespace. Due to this we do not want to // trim down the token list for the root element, but we do want to for // all other types of elements. if (type == ElementType.Root) { this.TrimTokens = false; } }
private bool CompareItems(CsElement first, CsElement second, bool foundFirst) { Param.AssertNotNull(first, "first"); Param.AssertNotNull(second, "second"); Param.Ignore(foundFirst); // We don't care about the order of accessors and we don't care about the order of empty elements. if ((first.ElementType != ElementType.EmptyElement && second.ElementType != ElementType.EmptyElement) && (first.ElementType != ElementType.Accessor || second.ElementType != ElementType.Accessor)) { // If the order turns out to be incorrect, determine which of the items is at fault. CsElement invalidElement = second; if (!foundFirst) { invalidElement = first; } // Check the item types to see if the second item type should appear before the first item type. if (first.ElementType > second.ElementType) { this.AddViolation( first, invalidElement.LineNumber, Rules.ElementsMustAppearInTheCorrectOrder, first.FriendlyPluralTypeText, second.FriendlyPluralTypeText); return(false); } else if (first.ElementType == second.ElementType) { // Make sure that both items have a declaration, or else we can go no further. if (first.Declaration != null && second.Declaration != null) { // Check the access modifiers to see if they are in the correct order. if (first.Declaration.AccessModifierType > second.Declaration.AccessModifierType) { // Special case for static constructors, which are always private but should still appear in front of all other constructors. if (first.ElementType != ElementType.Constructor || second.ElementType != ElementType.Constructor || !first.Declaration.ContainsModifier(CsTokenType.Static) || second.Declaration.ContainsModifier(CsTokenType.Static)) { // If one of the elements is partial and does not have an access modifier defined, and the element // is not a method, show a special message. Partial methods are not allowed to have modifiers and are // private by default. if ((!first.Declaration.AccessModifier && first.ElementType != ElementType.Method && first.Declaration.ContainsModifier(CsTokenType.Partial)) || (!second.Declaration.AccessModifier && second.ElementType != ElementType.Method && second.Declaration.ContainsModifier(CsTokenType.Partial))) { // Make sure to use the line number of the partial element which does not contain // an access modifier. CsElement elementWithoutAccessModifier = first; if (first.Declaration.AccessModifier || !first.Declaration.ContainsModifier(CsTokenType.Partial)) { elementWithoutAccessModifier = second; } this.AddViolation( elementWithoutAccessModifier, Rules.PartialElementsMustDeclareAccess, elementWithoutAccessModifier.FriendlyTypeText, elementWithoutAccessModifier.FriendlyPluralTypeText); } else { this.AddViolation( first, invalidElement.LineNumber, Rules.ElementsMustBeOrderedByAccess, OrderingRules.AccessModifierTypeString(first.Declaration.AccessModifierType), first.FriendlyPluralTypeText, OrderingRules.AccessModifierTypeString(second.Declaration.AccessModifierType), second.FriendlyPluralTypeText); } return(false); } } else if (first.Declaration.AccessModifierType == second.Declaration.AccessModifierType) { // order should be : // 0 const // 1 static readonly // 2 static non-readonly // 3 instance readonly // 4 instance non-readonly int firstOrder = GetElementOrder(first); int secondOrder = GetElementOrder(second); // Check to make sure that constant are first if (secondOrder == 0 && firstOrder > 0) { this.AddViolation(first, invalidElement.LineNumber, Rules.ConstantsMustAppearBeforeFields); return(false); } // Static Readonly fields are next if (secondOrder == 1 && firstOrder == 2) { this.AddViolation( first, invalidElement.LineNumber, Rules.StaticReadonlyElementsMustAppearBeforeStaticNonReadonlyElements, OrderingRules.AccessModifierTypeString(first.Declaration.AccessModifierType), first.FriendlyPluralTypeText, OrderingRules.AccessModifierTypeString(second.Declaration.AccessModifierType), second.FriendlyPluralTypeText); return(false); } if (secondOrder == 1 && firstOrder > 2) { this.AddViolation( first, invalidElement.LineNumber, Rules.StaticElementsMustAppearBeforeInstanceElements, OrderingRules.AccessModifierTypeString(first.Declaration.AccessModifierType), first.FriendlyPluralTypeText, OrderingRules.AccessModifierTypeString(second.Declaration.AccessModifierType), second.FriendlyPluralTypeText); return(false); } // Static non-Readonly fields are next if (secondOrder == 2 && firstOrder > 2) { this.AddViolation( first, invalidElement.LineNumber, Rules.StaticElementsMustAppearBeforeInstanceElements, OrderingRules.AccessModifierTypeString(first.Declaration.AccessModifierType), first.FriendlyPluralTypeText, OrderingRules.AccessModifierTypeString(second.Declaration.AccessModifierType), second.FriendlyPluralTypeText); return(false); } // instance readonly if (secondOrder == 3 && firstOrder > 3) { this.AddViolation( first, invalidElement.LineNumber, Rules.InstanceReadonlyElementsMustAppearBeforeInstanceNonReadonlyElements, OrderingRules.AccessModifierTypeString(first.Declaration.AccessModifierType), first.FriendlyPluralTypeText, OrderingRules.AccessModifierTypeString(second.Declaration.AccessModifierType), second.FriendlyPluralTypeText); return(false); } } else if (first.ElementType == ElementType.Constructor && second.ElementType == ElementType.Constructor && second.Declaration.ContainsModifier(CsTokenType.Static) && !first.Declaration.ContainsModifier(CsTokenType.Static)) { // If we have 2 constructors and the second one is static then they're in the wrong order, since static // constructors must always come in front of all instance constructors. this.AddViolation( first, invalidElement.LineNumber, Rules.StaticElementsMustAppearBeforeInstanceElements, OrderingRules.AccessModifierTypeString(first.Declaration.AccessModifierType), first.FriendlyPluralTypeText, OrderingRules.AccessModifierTypeString(second.Declaration.AccessModifierType), second.FriendlyPluralTypeText); return(false); } } } } return(true); }
private void CheckDeclarationKeywordOrder(CsElement element) { Param.AssertNotNull(element, "element"); int accessModifierIndex = -1; int staticIndex = -1; int otherWordIndex = -1; int index = 0; foreach (CsToken token in element.Declaration.Tokens) { CsTokenType type = token.CsTokenType; if (type == CsTokenType.Private || type == CsTokenType.Public || type == CsTokenType.Protected || type == CsTokenType.Internal) { if (accessModifierIndex == -1) { accessModifierIndex = index++; } } else if (type == CsTokenType.Static) { if (staticIndex == -1) { staticIndex = index++; } } else if (type != CsTokenType.WhiteSpace && type != CsTokenType.EndOfLine && type != CsTokenType.SingleLineComment && type != CsTokenType.MultiLineComment) { if (otherWordIndex == -1) { otherWordIndex = index++; } } } if (accessModifierIndex != -1) { if (staticIndex > -1 && staticIndex < accessModifierIndex) { this.AddViolation( element, Rules.DeclarationKeywordsMustFollowOrder, Strings.AccessModifier, string.Format(CultureInfo.InvariantCulture, "'{0}'", Strings.Static)); } if (otherWordIndex > -1 && otherWordIndex < accessModifierIndex) { this.AddViolation( element, Rules.DeclarationKeywordsMustFollowOrder, Strings.AccessModifier, string.Format(CultureInfo.InvariantCulture, "'{0}'", Strings.Other)); } } if (staticIndex > -1) { if (otherWordIndex > -1 && otherWordIndex < staticIndex) { this.AddViolation( element, Rules.DeclarationKeywordsMustFollowOrder, string.Format(CultureInfo.InvariantCulture, "'{0}'", Strings.Static), string.Format(CultureInfo.InvariantCulture, "'{0}'", Strings.Other)); } } // Check to make sure that 'protected' comes just before 'internal'. if (element.Declaration.AccessModifierType == AccessModifierType.ProtectedInternal) { bool foundProtected = false; foreach (CsToken token in element.Declaration.Tokens) { if (foundProtected) { if (token.CsTokenType == CsTokenType.Internal) { break; } else if (token.CsTokenType != CsTokenType.WhiteSpace) { this.AddViolation(element, Rules.ProtectedMustComeBeforeInternal); break; } } else { if (token.CsTokenType == CsTokenType.Protected) { foundProtected = true; } else if (token.CsTokenType == CsTokenType.Internal) { this.AddViolation(element, Rules.ProtectedMustComeBeforeInternal); break; } } } } }