/// <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> /// Checks the given call into Debug.Fail to ensure that it contains a valid debug message. /// </summary> /// <param name="element"> /// The parent element. /// </param> /// <param name="debugFailMethodCall"> /// The call to Debug.Fail. /// </param> private void CheckDebugFailMessage(CsElement element, MethodInvocationExpression debugFailMethodCall) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(debugFailMethodCall, "debugFailMethodCall"); // Extract the first argument. Argument firstArgument = null; foreach (Argument argument in debugFailMethodCall.Arguments) { firstArgument = argument; break; } if (firstArgument == null || firstArgument.Tokens.First == null) { // There is no message argument or the message argument is empty. this.AddViolation(element, debugFailMethodCall.LineNumber, Rules.DebugFailMustProvideMessageText); } else if (ArgumentTokensMatchStringEmpty(firstArgument)) { // The message argument contains an empty string or null. this.AddViolation(element, debugFailMethodCall.LineNumber, Rules.DebugFailMustProvideMessageText); } }
/// <summary> /// Checks the given call into Debug.Assert to ensure that it contains a valid debug message. /// </summary> /// <param name="element"> /// The parent element. /// </param> /// <param name="debugAssertMethodCall"> /// The call to Debug.Assert. /// </param> private void CheckDebugAssertMessage(CsElement element, MethodInvocationExpression debugAssertMethodCall) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(debugAssertMethodCall, "debugAssertMethodCall"); // Extract the second argument. Argument secondArgument = null; if (debugAssertMethodCall.Arguments.Count >= 2) { secondArgument = debugAssertMethodCall.Arguments[1]; } if (secondArgument == null || secondArgument.Tokens.First == null) { // There is no message argument or the message argument is empty. this.AddViolation(element, debugAssertMethodCall.LineNumber, Rules.DebugAssertMustProvideMessageText); } else if (ArgumentTokensMatchStringEmpty(secondArgument)) { // The message argument contains an empty string or null. this.AddViolation(element, debugAssertMethodCall.LineNumber, Rules.DebugAssertMustProvideMessageText); } }
/// <summary> /// Checks the given code analysis suppression call to ensure that it contains a justification parameter. /// </summary> /// <param name="element"> /// The element that contains the suppression attribute. /// </param> /// <param name="suppression"> /// The suppression to check. /// </param> private void CheckCodeAnalysisSuppressionForJustification(CsElement element, MethodInvocationExpression suppression) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(suppression, "suppression"); bool justification = false; foreach (Argument argument in suppression.Arguments) { if (argument.Expression.ExpressionType == ExpressionType.Assignment) { AssignmentExpression assignmentExpression = (AssignmentExpression)argument.Expression; if (assignmentExpression.LeftHandSide.Tokens.First.Value.Text.Equals("Justification", StringComparison.Ordinal)) { Expression rightHandSide = assignmentExpression.RightHandSide; if (rightHandSide == null || rightHandSide.Tokens == null) { break; } Node<CsToken> rightSideTokenNode = rightHandSide.Tokens.First; if (rightSideTokenNode == null) { break; } if (rightHandSide.ExpressionType == ExpressionType.MemberAccess) { justification = true; break; } if (rightSideTokenNode.Value.CsTokenType == CsTokenType.Other && rightHandSide.ExpressionType == ExpressionType.Literal) { justification = true; break; } if (rightSideTokenNode.Value.CsTokenType == CsTokenType.String && rightSideTokenNode.Value.Text != null && !IsEmptyString(rightSideTokenNode.Value.Text)) { justification = true; break; } } } } if (!justification) { this.AddViolation(element, suppression.LineNumber, Rules.CodeAnalysisSuppressionMustHaveJustification); } }
/// <summary> /// Determines whether the given method invocation expression contains a code analysis SuppressMessage call. /// </summary> /// <param name="expression"> /// The expression. /// </param> /// <returns> /// Returns true if the method is SuppressMessage. /// </returns> private static bool IsSuppressMessage(MethodInvocationExpression expression) { Param.AssertNotNull(expression, "expression"); Node<CsToken> first = expression.Name.Tokens.First; if (first != null) { string text = first.Value.Text; if (text.Equals("SuppressMessage", StringComparison.Ordinal) || text.Equals("SuppressMessageAttribute", StringComparison.Ordinal)) { return true; } string expressionText = expression.Name.Text; if (expressionText.EndsWith(".SuppressMessage", StringComparison.Ordinal) || expressionText.EndsWith(".SuppressMessageAttribute", StringComparison.Ordinal)) { return true; } if (text.Equals("System")) { if (expression.Name.Tokens.MatchTokens(new[] { "System", ".", "Diagnostics", ".", "CodeAnalysis", ".", "SuppressMessage" }) || expression.Name.Tokens.MatchTokens(new[] { "System", ".", "Diagnostics", ".", "CodeAnalysis", ".", "SuppressMessageAttribute" })) { return true; } } } return false; }
/// <summary> /// Reads a method access expression. /// </summary> /// <param name="methodName"> /// The name of the method being called. /// </param> /// <param name="previousPrecedence"> /// The precedence of the previous expression. /// </param> /// <param name="unsafeCode"> /// Indicates whether the code being parsed resides in an unsafe code block. /// </param> /// <returns> /// Returns the expression. /// </returns> private MethodInvocationExpression GetMethodInvocationExpression(Expression methodName, ExpressionPrecedence previousPrecedence, bool unsafeCode) { Param.AssertNotNull(methodName, "methodName"); Param.Ignore(previousPrecedence); Param.Ignore(unsafeCode); MethodInvocationExpression expression = null; if (CheckPrecedence(previousPrecedence, ExpressionPrecedence.Primary)) { Reference<ICodePart> expressionReference = new Reference<ICodePart>(); // The next symbol will be the opening parenthesis. Bracket openParenthesis = this.GetBracketToken(CsTokenType.OpenParenthesis, SymbolType.OpenParenthesis, expressionReference); Node<CsToken> openParenthesisNode = this.tokens.InsertLast(openParenthesis); // Get the argument list now. IList<Argument> argumentList = this.GetArgumentList(SymbolType.CloseParenthesis, expressionReference, unsafeCode); // Get the closing parenthesis. Bracket closeParenthesis = this.GetBracketToken(CsTokenType.CloseParenthesis, SymbolType.CloseParenthesis, expressionReference); Node<CsToken> closeParenthesisNode = this.tokens.InsertLast(closeParenthesis); openParenthesis.MatchingBracketNode = closeParenthesisNode; closeParenthesis.MatchingBracketNode = openParenthesisNode; // Pull out the first token from the method name. Debug.Assert(methodName.Tokens.First != null, "The method name should not be empty"); Node<CsToken> firstTokenNode = methodName.Tokens.First; // Create the token list for the method invocation expression. CsTokenList partialTokens = new CsTokenList(this.tokens, firstTokenNode, this.tokens.Last); // Create and return the expression. expression = new MethodInvocationExpression(partialTokens, methodName, argumentList); expressionReference.Target = expression; } return expression; }
/// <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(Element element, MethodInvocationExpression expression) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(expression, "expression"); if (!expression.Generated) { ArgumentList argumentList = expression.ArgumentList; if (argumentList != null) { this.CheckParameters(element, argumentList, new ArgumentListWrapper(argumentList), expression.LineNumber, TokenType.OpenParenthesis, TokenType.CloseParenthesis); } } }
/// <summary> /// Extracts the CheckID for the rule being suppressed, from the given Code Analysis SuppressMessage attribute expression. /// </summary> /// <param name="codeAnalysisAttributeExpression"> /// The expression to parse. /// </param> /// <param name="ruleId"> /// Returns the rule ID. /// </param> /// <param name="ruleName"> /// Returns the rule name. /// </param> /// <param name="ruleNamespace"> /// Returns the namespace that contains the rule. /// </param> /// <returns> /// Returns true if the ID, name, and namespace were successfully extracted from the suppression. /// </returns> private static bool TryCrackCodeAnalysisSuppression( MethodInvocationExpression codeAnalysisAttributeExpression, out string ruleId, out string ruleName, out string ruleNamespace) { Param.AssertNotNull(codeAnalysisAttributeExpression, "codeAnalysisAttributeExpression"); // Initialize all out fields to null. ruleId = ruleName = ruleNamespace = null; if (codeAnalysisAttributeExpression.Arguments != null && codeAnalysisAttributeExpression.Arguments.Count >= 2) { // The rule namespace sits in the first argument. ruleNamespace = ExtractStringFromAttributeExpression(codeAnalysisAttributeExpression.Arguments[0].Expression); if (string.IsNullOrEmpty(ruleNamespace)) { return false; } // Here we support the old SupressMessage format for Microsoft.StyleCop if (ruleNamespace.StartsWith("Microsoft.StyleCop.")) { ruleNamespace = ruleNamespace.Substring("Microsoft.".Length); } // The checkID and rule name sit in the second argument. string nameAndId = ExtractStringFromAttributeExpression(codeAnalysisAttributeExpression.Arguments[1].Expression); if (string.IsNullOrEmpty(nameAndId)) { return false; } // When the nameAndId field just contains a *, this means to supress all rules in the given namespace. if (nameAndId == "*") { ruleId = "*"; return true; } // Split the rule name and ID. int separatorIndex = nameAndId.IndexOf(':'); if (separatorIndex == -1) { return false; } ruleId = nameAndId.Substring(0, separatorIndex); ruleName = nameAndId.Substring(separatorIndex + 1, nameAndId.Length - separatorIndex - 1); return ruleId.Length > 0 && ruleName.Length > 0; } return false; }
/// <summary> /// Checks the given call into Debug.Fail to ensure that it contains a valid debug message. /// </summary> /// <param name="element">The parent element.</param> /// <param name="debugFailMethodCall">The call to Debug.Fail.</param> private void CheckDebugFailMessage(Element element, MethodInvocationExpression debugFailMethodCall) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(debugFailMethodCall, "debugFailMethodCall"); // Extract the first argument. Argument firstArgument = null; if (debugFailMethodCall.ArgumentList != null && debugFailMethodCall.ArgumentList.Count > 0) { firstArgument = debugFailMethodCall.ArgumentList[0]; } if (firstArgument == null) { // There is no message argument. this.AddViolation(element, debugFailMethodCall.LineNumber, Rules.DebugFailMustProvideMessageText); } else { Token firstArgumentStartToken = firstArgument.FindFirstDescendentToken(); if (firstArgumentStartToken == null) { // The message argument is empty. this.AddViolation(element, debugFailMethodCall.LineNumber, Rules.DebugFailMustProvideMessageText); } else if (firstArgumentStartToken.TokenType == TokenType.String && IsEmptyString(firstArgumentStartToken.Text)) { // The message argument contains an empty string. this.AddViolation(element, debugFailMethodCall.LineNumber, Rules.DebugFailMustProvideMessageText); } } }
/// <summary> /// Checks the given code analysis suppression call to ensure that it contains a justifiction argument. /// </summary> /// <param name="element">The element that contains the suppression attribute.</param> /// <param name="suppression">The suppression to check.</param> private void CheckCodeAnalysisSuppressionForJustification(Element element, MethodInvocationExpression suppression) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(suppression, "suppression"); bool justifiction = false; if (suppression.ArgumentList != null) { foreach (Argument argument in suppression.ArgumentList.Arguments) { if (argument.Expression.ExpressionType == ExpressionType.Assignment) { AssignmentExpression assignmentExpression = (AssignmentExpression)argument.Expression; Token firstAssignmentExpressionToken = assignmentExpression.LeftHandSide.FindFirstDescendentToken(); if (firstAssignmentExpressionToken != null && firstAssignmentExpressionToken.Text.Equals("Justification", StringComparison.Ordinal)) { Token rightSideToken = assignmentExpression.RightHandSide.FindFirstDescendentToken(); if (rightSideToken != null && rightSideToken.TokenType == TokenType.String && rightSideToken.Text != null && !IsEmptyString(rightSideToken.Text)) { justifiction = true; break; } } } } if (!justifiction) { this.AddViolation(element, suppression.LineNumber, Rules.CodeAnalysisSuppressionMustHaveJustification); } } }
/// <summary> /// Determines whether the given method invocation expression contains a code analysis SuppressMessage call. /// </summary> /// <param name="expression">The expression.</param> /// <returns>Returns true if the method is SuppressMessage.</returns> private static bool IsSuppressMessage(MethodInvocationExpression expression) { Param.AssertNotNull(expression, "expression"); Token first = expression.Name.FindFirstDescendentToken(); if (first != null) { if (first.Text.Equals("SuppressMessage", StringComparison.Ordinal)) { return true; } if (first.Text.Equals("System")) { return expression.Name.MatchTokensFrom(first, "System", ".", "Diagnostics", ".", "CodeAnalysis", ".", "SuppressMessage"); } } return false; }
/// <summary> /// The save. /// </summary> /// <param name="methodInvocationExpression"> /// The method invocation expression. /// </param> private void Save(MethodInvocationExpression methodInvocationExpression) { if (methodInvocationExpression.Name.Text == "base") { this.Save(this.currentBaseClass, this.cppWriter, SavingOptions.RemovePointer); } else { if (methodInvocationExpression.Name is LiteralExpression && methodInvocationExpression.Name.Text.Contains('<')) { // you need to process generic types this.Save(methodInvocationExpression.Name.Text, this.cppWriter, SavingOptions.RemovePointer); } else { @switch(methodInvocationExpression.Name); } } this.cppWriter.Write("("); @switch(methodInvocationExpression.Arguments); this.cppWriter.Write(")"); }
/// <summary> /// Extracts the CheckID for the rule being suppressed, from the given Code Analysis SuppressMessage attribute expression. /// </summary> /// <param name="codeAnalysisAttributeExpression">The expression to parse.</param> /// <param name="ruleId">Returns the rule ID.</param> /// <param name="ruleName">Returns the rule name.</param> /// <param name="ruleNamespace">Returns the namespace that contains the rule.</param> /// <returns>Returns true if the ID, name, and namespace were successfully extracted from the suppression.</returns> private static bool TryCrackCodeAnalysisSuppression(MethodInvocationExpression codeAnalysisAttributeExpression, out string ruleId, out string ruleName, out string ruleNamespace) { Param.AssertNotNull(codeAnalysisAttributeExpression, "codeAnalysisAttributeExpression"); // Initialize all out fields to null. ruleId = ruleName = ruleNamespace = null; if (codeAnalysisAttributeExpression.ArgumentList != null && codeAnalysisAttributeExpression.ArgumentList.Count >= 2) { // The rule namespace sits in the first argument. ruleNamespace = ExtractStringFromAttributeExpression(codeAnalysisAttributeExpression.ArgumentList[0].Expression); if (string.IsNullOrEmpty(ruleNamespace)) { return false; } // The checkID and rule name sit in the second argument. string nameAndId = ExtractStringFromAttributeExpression(codeAnalysisAttributeExpression.ArgumentList[1].Expression); if (string.IsNullOrEmpty(nameAndId)) { return false; } int separatorIndex = nameAndId.IndexOf(':'); if (separatorIndex == -1) { return false; } ruleId = nameAndId.Substring(0, separatorIndex); ruleName = nameAndId.Substring(separatorIndex + 1, nameAndId.Length - separatorIndex - 1); return ruleId.Length > 0 && ruleName.Length > 0; } return false; }
/// <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> /// 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); } } } } }