/// <summary> /// Initializes a new instance of the Argument class. /// </summary> /// <param name="name"> /// The optional name of the argument. /// </param> /// <param name="modifiers"> /// Modifiers applied to this argument. /// </param> /// <param name="argumentExpression"> /// The expression that forms the body of the argument. /// </param> /// <param name="location"> /// The location of the argument in the code. /// </param> /// <param name="parent"> /// The parent code part. /// </param> /// <param name="tokens"> /// The tokens that form the argument. /// </param> /// <param name="generated"> /// Indicates whether the argument is located within a block of generated code. /// </param> internal Argument( CsToken name, ParameterModifiers modifiers, Expression argumentExpression, CodeLocation location, Reference <ICodePart> parent, CsTokenList tokens, bool generated) { Param.Ignore(name); Param.Ignore(modifiers); Param.AssertNotNull(argumentExpression, "argumentExpression"); Param.AssertNotNull(location, "location"); Param.AssertNotNull(parent, "parent"); Param.Ignore(tokens); Param.Ignore(generated); this.name = name; this.modifiers = modifiers; this.argumentExpression = argumentExpression; this.location = location; this.parent = parent; this.tokens = tokens; this.generated = generated; }
/////// <summary> /////// Joins the locations of the two tokens. /////// </summary> /////// <param name="token1">The first token.</param> /////// <param name="location2">The second location.</param> /////// <returns>Returns the joined locations.</returns> ////internal static CodeLocation JoinLocations(Node<CsToken> token1, CodeLocation location2) ////{ //// Param.Ignore(token1, location2); //// return CsToken.JoinLocations(token1 == null ? null : token1.Value, location2); ////} /// <summary> /// Joins the locations of the two tokens. /// </summary> /// <param name="token1"> /// The first token. /// </param> /// <param name="token2"> /// The second token. /// </param> /// <returns> /// Returns the joined locations. /// </returns> internal static CodeLocation JoinLocations(CsToken token1, CsToken token2) { Param.AssertNotNull(token1, "token1"); Param.AssertNotNull(token2, "token2"); return(CodeLocation.Join(token1.Location, token2.Location)); }
/// <summary> /// Checks a string to determine whether it is using an incorrect empty string notation. /// </summary> /// <param name="stringNode"> /// The node containing the string to check. /// </param> private void CheckEmptyString(Node <CsToken> stringNode) { Param.AssertNotNull(stringNode, "stringNode"); CsToken @string = stringNode.Value; Debug.Assert(@string.CsTokenType == CsTokenType.String, "The token must be a string."); if (string.Equals(@string.Text, "\"\"", StringComparison.Ordinal) || string.Equals(@string.Text, "@\"\"", StringComparison.Ordinal) || string.Equals(@string.Text, "$\"\"", StringComparison.Ordinal)) { // Look at the previous non-whitespace 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. // We also check that the node is not part of a method parameter as these must be "" also. Node <CsToken> previousToken = null; for (Node <CsToken> previousNode = stringNode.Previous; previousNode != null; previousNode = previousNode.Previous) { if (previousNode.Value.CsTokenType != CsTokenType.WhiteSpace && previousNode.Value.CsTokenType != CsTokenType.EndOfLine && previousNode.Value.CsTokenType != CsTokenType.SingleLineComment && previousNode.Value.CsTokenType != CsTokenType.MultiLineComment) { previousToken = previousNode; break; } } if (previousToken == null || (previousToken.Value.CsTokenType != CsTokenType.Case && !IsConstVariableDeclaration(previousToken) && !IsMethodParameterDeclaration(previousToken))) { this.AddViolation(@string.FindParentElement(), @string.LineNumber, Rules.UseStringEmptyForEmptyStrings); } } }
/// <summary> /// Checks the generic type uses the shorthand declaration format. /// </summary> /// <param name="type"> /// The <see cref="CsToken"/> to check. /// </param> private void CheckShorthandForNullableTypes(CsToken type) { Param.AssertNotNull(type, "type"); Debug.Assert(type.CsTokenClass == CsTokenClass.GenericType, "Expected a generic type."); GenericType genericType = (GenericType)type; // Check the declaration of the generic type for longhand, but allow Nullable<> which has no shorthand if (genericType.ChildTokens.Count > 0 && Utils.TokenContainNullable(genericType.ChildTokens.First)) { if (genericType.Parent == null || !(genericType.Parent is TypeofExpression)) { if (genericType.Parent is Method) { Method parentMethod = genericType.Parent as Method; if (parentMethod.Name == parentMethod.FriendlyTypeText + " " + genericType.Text) { return; } } this.AddViolation(genericType.FindParentElement(), genericType.LineNumber, Rules.UseShorthandForNullableTypes); } } else { // Check the parameters of the generic type because the declaration may be nested foreach ( GenericTypeParameter genericTypeParameter in genericType.GenericTypesParameters.Where(token => token.Type.CsTokenClass == CsTokenClass.GenericType)) { this.CheckShorthandForNullableTypes(genericTypeParameter.Type); } } }
/// <summary> /// Determines whether the given word is the name of a local variable. /// </summary> /// <param name="word"> /// The name to check. /// </param> /// <param name="item"> /// The token containing the word. /// </param> /// <param name="parent"> /// The code unit that the word appears in. /// </param> /// <returns> /// True if the word is the name of a local variable, false if not. /// </returns> private static bool IsLocalMember(string word, CsToken item, ICodeUnit parent) { Param.AssertValidString(word, "word"); Param.AssertNotNull(item, "item"); Param.AssertNotNull(parent, "parent"); while (parent != null) { // Check to see if the name matches a local variable. if (ContainsVariable(parent.Variables, word, item)) { return(true); } // If the parent is an element, do not look any higher up the stack than this. if (parent.CodePartType == CodePartType.Element) { break; } // Check to see whether the variable is defined within the parent. parent = parent.Parent as ICodeUnit; } return(false); }
/// <summary> /// Checks the built-in types and empty strings within a document. /// </summary> /// <param name="document"> /// The document containing the tokens. /// </param> /// <param name="settings"> /// The current settings. /// </param> private void IterateTokenList(CsDocument document, Settings settings) { Param.AssertNotNull(document, "document"); Param.Ignore(settings); for (Node <CsToken> tokenNode = document.Tokens.First; tokenNode != null; tokenNode = tokenNode.Next) { CsToken token = tokenNode.Value; if (token.CsTokenClass == CsTokenClass.Type || token.CsTokenClass == CsTokenClass.GenericType) { // Check that the type is using the built-in types, if applicable. this.CheckBuiltInType(tokenNode, document); if (token.CsTokenClass == CsTokenClass.GenericType) { this.CheckShorthandForNullableTypes(tokenNode.Value); } } else if (token.CsTokenType == CsTokenType.String) { // Check that the string is not using the empty string "" syntax. this.CheckEmptyString(tokenNode); } else if (token.CsTokenClass == CsTokenClass.RegionDirective && settings.DoNotUseRegions) { Region region = (Region)token; if (region.Beginning && !region.Generated && !region.IsGeneratedCodeRegion) { // There should not be any regions in the code. this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.DoNotUseRegions); } } } }
/// <summary> /// Parses the given literal token. /// </summary> /// <param name="tokenNode"> /// 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( Node <CsToken> tokenNode, Expression expression, Expression parentExpression, CsElement parentElement, ClassBase parentClass, Dictionary <string, List <CsElement> > members) { Param.AssertNotNull(tokenNode, "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 (!(tokenNode.Value is TypeToken) || tokenNode.Value.CsTokenClass == CsTokenClass.GenericType) { // If the name starts with a dot, ignore it. if (!tokenNode.Value.Text.StartsWith(".", StringComparison.Ordinal)) { if (tokenNode.Value.Text == "base" && parentExpression != null) { CsToken name = Utils.ExtractBaseClassMemberName(parentExpression, tokenNode); if (name != null) { if (!this.IsBaseRequired(name.Text, parentClass, members)) { this.AddViolation(parentElement, name.Location, Rules.DoNotPrefixCallsWithBaseUnlessLocalImplementationExists, name); } } } else if (tokenNode.Value.Text != "this") { if (this.IsThisRequired(tokenNode, expression, parentClass, members)) { if ((parentClass.BaseClass != string.Empty) || (tokenNode.Value.Text == "Equals" || tokenNode.Value.Text == "ReferenceEquals")) { string className = parentClass.FullyQualifiedName.SubstringAfterLast('.'); if (parentClass.BaseClass != string.Empty) { className = className + ".' or '" + parentClass.BaseClass; } this.AddViolation(parentElement, tokenNode.Value.Location, Rules.PrefixCallsCorrectly, tokenNode.Value.Text, className); } else { this.AddViolation(parentElement, tokenNode.Value.Location, Rules.PrefixLocalCallsWithThis, tokenNode.Value.Text); } } } } } }
/// <summary> /// Initializes a new instance of the FileHeader class. /// </summary> /// <param name="headerText"> /// The header text. /// </param> /// <param name="tokens"> /// The collection of tokens in the header. /// </param> /// <param name="parent"> /// The parent of the header. /// </param> internal FileHeader(string headerText, CsTokenList tokens, Reference <ICodePart> parent) { Param.AssertNotNull(headerText, "headerText"); Param.AssertNotNull(tokens, "tokens"); Param.AssertNotNull(parent, "parent"); this.headerText = headerText; this.tokens = tokens; this.parent = parent; this.location = this.tokens.First != null?CsToken.JoinLocations(this.tokens.First, this.tokens.Last) : CodeLocation.Empty; // Attempt to load this into an Xml document. try { if (this.headerText.Length > 0) { this.headerXml = string.Format(CultureInfo.InvariantCulture, "<root>{0}</root>", HtmlEncode(this.headerText)); XmlDocument doc = new XmlDocument(); doc.LoadXml(this.headerXml); // Check whether the header has the autogenerated tag. if (doc.DocumentElement != null) { XmlNode node = doc.DocumentElement["autogenerated"]; if (node != null) { // Set this as generated code. this.generated = true; } else { node = doc.DocumentElement["auto-generated"]; if (node != null) { // Set this as generated code. this.generated = true; } } StringCollection unstyledElements = new StringCollection(); unstyledElements.AddRange(new[] { "unstyled", "stylecopoff", "nostyle" }); XmlNodeList childNodes = doc.DocumentElement.ChildNodes; if (childNodes.Cast <XmlNode>().Any(xmlNode => unstyledElements.Contains(xmlNode.Name.ToLowerInvariant()))) { this.UnStyled = true; } } } } catch (XmlException) { } }
/// <summary> /// Checks the built-in types and empty strings within a document. /// </summary> /// <param name="document"> /// The document containing the tokens. /// </param> /// <param name="settings"> /// The current settings. /// </param> private void IterateTokenList(CsDocument document, Settings settings) { Param.AssertNotNull(document, "document"); Param.Ignore(settings); for (Node <CsToken> tokenNode = document.Tokens.First; tokenNode != null; tokenNode = tokenNode.Next) { CsToken token = tokenNode.Value; if (token.CsTokenClass == CsTokenClass.Type || token.CsTokenClass == CsTokenClass.GenericType) { // Check that the type is using the built-in types, if applicable. this.CheckBuiltInType(tokenNode, document); if (token.CsTokenClass == CsTokenClass.GenericType) { this.CheckShorthandForNullableTypes(tokenNode.Value); } } else if (token.CsTokenType == CsTokenType.String) { // Check that the string is not using the empty string "" syntax. this.CheckEmptyString(tokenNode); } else if (token.CsTokenClass == CsTokenClass.RegionDirective && settings.DoNotUseRegions) { Region region = (Region)token; if (region.Beginning && !region.Generated && !region.IsGeneratedCodeRegion) { // There should not be any regions in the code. this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.DoNotUseRegions); } } else if (settings.AvoidStringFormatUseStringInterpolation && token.CsTokenType == CsTokenType.Other && token.Text == "Format") { // Check this rule only if project target framework version is greater or equal 4.6 if (document.SourceCode.Project.TargetFrameworkVersion >= 4.6) { MemberAccessExpression expression = token.Parent?.Parent as MemberAccessExpression; // Check if literal expression is not null and check text to avoid user's custom code like enumeration. if (expression != null && expression.Text == "string.Format") { CsToken region = (CsToken)token; if (!region.Generated) { // There should not be any regions in the code. this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.AvoidStringFormatUseStringInterpolation); } } } } } }
/// <summary> /// Checks the token text matches a ReSharper suppression. /// </summary> /// <param name="token"> /// The token to check. /// </param> /// <returns> /// True if its a ReSharper token otherwise false. /// </returns> public static bool IsAReSharperComment(CsToken token) { Param.AssertNotNull(token, "token"); if (token.CsTokenType != CsTokenType.MultiLineComment && token.CsTokenType != CsTokenType.SingleLineComment && token.CsTokenType != CsTokenType.XmlHeader && token.CsTokenType != CsTokenType.XmlHeaderLine) { return(false); } string tokenText = token.Text; return(tokenText.StartsWith("// ReSharper disable ") || tokenText.StartsWith("// ReSharper restore ")); }
/// <summary> /// Initializes a new instance of the TypeParameterConstraintClause class. /// </summary> /// <param name="tokens"> /// The list of tokens that form the constraint. /// </param> /// <param name="type"> /// The type being constrained. /// </param> /// <param name="constraints"> /// The list of constraints on the type, if any. /// </param> /// <param name="parent"> /// The parent of the constraint clause. /// </param> internal TypeParameterConstraintClause(CsTokenList tokens, CsToken type, ICollection <CsToken> constraints, Reference <ICodePart> parent) { Param.AssertNotNull(tokens, "tokens"); Param.AssertNotNull(type, "type"); Param.Ignore(constraints); Param.AssertNotNull(parent, "parent"); this.tokens = tokens; this.type = type; this.constraints = constraints; this.parent = parent; Debug.Assert(this.constraints == null || this.constraints.IsReadOnly, "Constraints must be read-only"); this.tokens.Trim(); Debug.Assert(this.tokens.First != null, "The type parameter constraint claus should not be empty."); }
/// <summary> /// Initializes a new instance of the TypeParameterConstraintClause class. /// </summary> /// <param name="tokens"> /// The list of tokens that form the constraint. /// </param> /// <param name="type"> /// The type being constrained. /// </param> /// <param name="constraints"> /// The list of constraints on the type, if any. /// </param> /// <param name="parent"> /// The parent of the constraint clause. /// </param> internal TypeParameterConstraintClause(CsTokenList tokens, CsToken type, ICollection<CsToken> constraints, Reference<ICodePart> parent) { Param.AssertNotNull(tokens, "tokens"); Param.AssertNotNull(type, "type"); Param.Ignore(constraints); Param.AssertNotNull(parent, "parent"); this.tokens = tokens; this.type = type; this.constraints = constraints; this.parent = parent; Debug.Assert(this.constraints == null || this.constraints.IsReadOnly, "Constraints must be read-only"); this.tokens.Trim(); Debug.Assert(this.tokens.First != null, "The type parameter constraint claus should not be empty."); }
/// <summary> /// Checks the curly at the end of a statement which trails the rest of the statement. /// </summary> /// <param name="element"> /// The element containing the statement. /// </param> /// <param name="statement"> /// The statement to check. /// </param> private void CheckTrailingStatementCurlyBracketPlacement(CsElement element, Statement statement) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(statement, "statement"); Node <CsToken> curlyBracket = GetClosingBracketFromStatement(statement); if (curlyBracket != null) { // Find the next token after this closing curly bracket. CsToken nextToken = GetNextToken(curlyBracket.Next, statement.Tokens.MasterList); if (nextToken != null) { this.CheckTokenPrecedingOrFollowingCurlyBracket(element, nextToken); } } }
/// <summary> /// Checks a method or method invocation to ensure that the opening bracket is /// on the same line as the method declaration. /// </summary> /// <param name="element"> /// The element containing the expression. /// </param> /// <param name="parameterListTokens"> /// The tokens in the parameter list. /// </param> /// <param name="openingBracketType"> /// The type of the bracket that opens the parameter list. /// </param> /// <param name="textToUseForContainingElement"> /// The text to use in the violation. /// </param> /// <returns> /// Returns the opening bracket. /// </returns> private Node <CsToken> CheckMethodOpeningBracket( CsElement element, CsTokenList parameterListTokens, CsTokenType openingBracketType, string textToUseForContainingElement) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(parameterListTokens, "parameterListTokens"); Param.Ignore(openingBracketType); // Find the opening bracket. Node <CsToken> openingBracketNode = null; for (Node <CsToken> tokenNode = parameterListTokens.First; tokenNode != null; tokenNode = tokenNode.Next) { if (tokenNode.Value.CsTokenType == openingBracketType) { openingBracketNode = tokenNode; break; } } CsToken lastWord = null; if (openingBracketNode != null) { // Find the last word before the opening bracket. for (Node <CsToken> tokenNode = openingBracketNode.Previous; tokenNode != null; tokenNode = tokenNode.Previous) { if (tokenNode.Value.CsTokenType != CsTokenType.WhiteSpace && tokenNode.Value.CsTokenType != CsTokenType.EndOfLine && tokenNode.Value.CsTokenType != CsTokenType.SingleLineComment && tokenNode.Value.CsTokenType != CsTokenType.MultiLineComment) { lastWord = tokenNode.Value; break; } } } if (lastWord != null) { if (openingBracketNode.Value.LineNumber != lastWord.LineNumber) { this.AddViolation(element, openingBracketNode.Value.LineNumber, Rules.OpeningParenthesisMustBeOnDeclarationLine, textToUseForContainingElement); } } return(openingBracketNode); }
/// <summary> /// Checks the token that follows or precedes a curly bracket in a blocked statement to verify /// that there is no comment or region embedded within the statement. /// </summary> /// <param name="element"> /// The element containing the statement. /// </param> /// <param name="previousOrNextToken"> /// The previous or next token. /// </param> private void CheckTokenPrecedingOrFollowingCurlyBracket(CsElement element, CsToken previousOrNextToken) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(previousOrNextToken, "previousOrNextToken"); if (previousOrNextToken.CsTokenType == CsTokenType.MultiLineComment || previousOrNextToken.CsTokenType == CsTokenType.SingleLineComment || previousOrNextToken.CsTokenType == CsTokenType.XmlHeader || previousOrNextToken.CsTokenType == CsTokenType.XmlHeaderLine) { if (!Utils.IsAReSharperComment(previousOrNextToken)) { this.AddViolation(element, previousOrNextToken.LineNumber, Rules.BlockStatementsMustNotContainEmbeddedComments); } } else if (previousOrNextToken.CsTokenType == CsTokenType.PreprocessorDirective && previousOrNextToken is Region) { this.AddViolation(element, previousOrNextToken.LineNumber, Rules.BlockStatementsMustNotContainEmbeddedRegions); } }
/// <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); } } }
//public override void VisitElement(CsElement element, CsElement parentElement, object context) //{ // if (element.ElementType == ElementType.Root) // { // element. // } //} /// <summary> /// Visits the token. /// </summary> /// <param name="document">The document.</param> /// <param name="token">The token.</param> public override void VisitToken(CodeDocument document, CsToken token) { if (token.CsTokenType == CsTokenType.EndOfLine && this.lastTokenWasWhitespace) { this.SourceAnalyzer.AddViolation(document.DocumentContents, token.LineNumber, ContribRule.NoTrailingWhiteSpace); } if (this.isBeginningOfLine) { if ((token.CsTokenType == CsTokenType.WhiteSpace) && (!SpacingAnalyzer.IsValidIndentationWhitespace(token.Text))) { this.SourceAnalyzer.AddViolation(document.DocumentContents, token.LineNumber, ContribRule.IndentUsingTabs); } this.isBeginningOfLine = false; } this.lastTokenWasWhitespace = token.CsTokenType == CsTokenType.WhiteSpace; if (token.CsTokenType == CsTokenType.EndOfLine) this.isBeginningOfLine = true; }
/// <summary> /// Reads an expression starting with an unknown word. /// </summary> /// <returns>Returns the expression.</returns> private LiteralExpression GetConditionalPreprocessorConstantExpression() { // Get the first symbol. Symbol symbol = this.symbols.Peek(1); Debug.Assert(symbol != null && symbol.SymbolType == SymbolType.Other, "Expected a text symbol"); Reference <ICodePart> expressionReference = new Reference <ICodePart>(); // Convert the symbol to a token. this.symbols.Advance(); CsToken literalToken = new CsToken(symbol.Text, CsTokenType.Other, symbol.Location, expressionReference, this.symbols.Generated); Node <CsToken> literalTokenNode = this.tokens.InsertLast(literalToken); // Create a literal expression from this token. LiteralExpression expression = new LiteralExpression(this.tokens, literalTokenNode); expressionReference.Target = expression; return(expression); }
private static IEnumerable<CsToken> Flatten(CsToken token) { var typeToken = token as TypeToken; if (typeToken != null) { return typeToken.ChildTokens.SelectMany(child => Flatten(child)); } var attributeToken = token as Attribute; if (attributeToken != null) { return attributeToken.ChildTokens.SelectMany(child => Flatten(child)); } var xmlHeader = token as XmlHeader; if (xmlHeader != null) { return xmlHeader.ChildTokens.SelectMany(child => Flatten(child)); } return Enumerable.Repeat(token, 1); }
/// <summary> /// Gets the location of one of the parameters in the list. /// </summary> /// <param name="index"> /// The index of a parameter in the list. /// </param> /// <returns> /// Returns the location of the parameters. /// </returns> public CodeLocation Location(int index) { Param.AssertValueBetween(index, 0, this.parameters.Count - 1, "index"); // The location must be calculated by finding the first and last tokens // in the parameter and joining their locations. CsTokenList tokens = this.parameters[index].Tokens; CsToken firstToken = null; for (Node <CsToken> tokenNode = tokens.First.Previous; tokenNode != null; tokenNode = tokenNode.Previous) { if (tokenNode.Value.CsTokenType == CsTokenType.Comma || tokenNode.Value.CsTokenType == CsTokenType.OpenSquareBracket || tokenNode.Value.CsTokenType == CsTokenType.OpenParenthesis) { // We've found the start of the parameter list. Now move forward to find the first word. for (tokenNode = tokenNode.Next; tokenNode != null; tokenNode = tokenNode.Next) { if (tokenNode.Value.CsTokenType != CsTokenType.Attribute && tokenNode.Value.CsTokenType != CsTokenType.WhiteSpace && tokenNode.Value.CsTokenType != CsTokenType.EndOfLine && tokenNode.Value.CsTokenType != CsTokenType.SingleLineComment && tokenNode.Value.CsTokenType != CsTokenType.MultiLineComment) { firstToken = tokenNode.Value; break; } } break; } } if (firstToken != null) { return(CodeLocation.Join(firstToken.Location, tokens.Last.Value.Location)); } return(this.parameters[index].Location); }
/// <summary> /// Determines whether the type of the given token is allowed /// to appear after a closing parenthesis, with no space between /// the parenthesis and the token. /// </summary> /// <param name="token"> /// The token to check. /// </param> /// <returns> /// True if it is allowed; false otherwise. /// </returns> private static bool IsAllowedAfterClosingParenthesis(CsToken token) { Param.AssertNotNull(token, "token"); CsTokenType type = token.CsTokenType; if (type == CsTokenType.CloseParenthesis || type == CsTokenType.OpenParenthesis || type == CsTokenType.CloseSquareBracket || type == CsTokenType.OpenSquareBracket || type == CsTokenType.CloseAttributeBracket || type == CsTokenType.Semicolon || type == CsTokenType.Comma) { return true; } else if (type == CsTokenType.OperatorSymbol) { OperatorSymbol symbol = (OperatorSymbol)token; if (symbol.SymbolType == OperatorType.Decrement || symbol.SymbolType == OperatorType.Increment || symbol.SymbolType == OperatorType.MemberAccess || symbol.SymbolType == OperatorType.Pointer || symbol.SymbolType == OperatorType.NullConditional) { return true; } } return false; }
/// <summary> /// Determines whether the given token is preceded by a member access symbol. /// </summary> /// <param name="literalTokenNode"> /// The token to check. /// </param> /// <param name="masterList"> /// The list containing the token. /// </param> /// <returns> /// Returns true if the token is preceded by a member access symbol. /// </returns> private static bool IsLiteralTokenPrecededByMemberAccessSymbol(Node <CsToken> literalTokenNode, MasterList <CsToken> masterList) { Param.AssertNotNull(literalTokenNode, "literalTokenNode"); Param.AssertNotNull(masterList, "masterList"); // Get the previous non-whitespace token. CsToken previousToken = ReadabilityRules.GetPreviousToken(literalTokenNode.Previous, masterList); if (previousToken == null) { return(false); } if (previousToken.CsTokenType == CsTokenType.OperatorSymbol) { OperatorSymbol symbol = (OperatorSymbol)previousToken; if (symbol.SymbolType == OperatorType.MemberAccess || symbol.SymbolType == OperatorType.Pointer || symbol.SymbolType == OperatorType.QualifiedAlias) { return(true); } } return(false); }
/// <summary> /// Determines whether the given word is the name of a local variable. /// </summary> /// <param name="word"> /// The name to check. /// </param> /// <param name="item"> /// The token containing the word. /// </param> /// <param name="parent"> /// The code unit that the word appears in. /// </param> /// <returns> /// True if the word is the name of a local variable, false if not. /// </returns> private static bool IsLocalMember(string word, CsToken item, ICodeUnit parent) { Param.AssertValidString(word, "word"); Param.AssertNotNull(item, "item"); Param.AssertNotNull(parent, "parent"); while (parent != null) { // Check to see if the name matches a local variable. if (ContainsVariable(parent.Variables, word, item)) { return true; } // If the parent is an element, do not look any higher up the stack than this. if (parent.CodePartType == CodePartType.Element) { break; } // Check to see whether the variable is defined within the parent. parent = parent.Parent as ICodeUnit; } return false; }
/// <summary> /// Reads the next expression from a conditional preprocessor directive. /// </summary> /// <param name="sourceCode"> /// The source code. /// </param> /// <param name="previousPrecedence"> /// The precedence of the expression just before this one. /// </param> /// <returns> /// Returns the expression. /// </returns> private Expression GetNextConditionalPreprocessorExpression(SourceCode sourceCode, ExpressionPrecedence previousPrecedence) { Param.AssertNotNull(sourceCode, "sourceCode"); Param.Ignore(previousPrecedence); Reference<ICodePart> parentReference = new Reference<ICodePart>(); // Move past comments and whitepace. this.AdvanceToNextConditionalDirectiveCodeSymbol(parentReference); // Saves the next expression. Expression expression = null; // Get the next symbol. Symbol symbol = this.symbols.Peek(1); if (symbol != null) { switch (symbol.SymbolType) { case SymbolType.Other: expression = this.GetConditionalPreprocessorConstantExpression(); break; case SymbolType.Not: expression = this.GetConditionalPreprocessorNotExpression(sourceCode, parentReference); break; case SymbolType.OpenParenthesis: expression = this.GetConditionalPreprocessorParenthesizedExpression(sourceCode, parentReference); break; case SymbolType.False: this.symbols.Advance(); Reference<ICodePart> falseExpressionReference = new Reference<ICodePart>(); CsToken token = new CsToken(symbol.Text, CsTokenType.False, symbol.Location, falseExpressionReference, this.symbols.Generated); Node<CsToken> tokenNode = this.tokens.InsertLast(token); expression = new LiteralExpression(new CsTokenList(this.tokens, tokenNode, tokenNode), tokenNode); falseExpressionReference.Target = expression; break; case SymbolType.True: this.symbols.Advance(); Reference<ICodePart> trueExpressionReference = new Reference<ICodePart>(); token = new CsToken(symbol.Text, CsTokenType.True, symbol.Location, trueExpressionReference, this.symbols.Generated); tokenNode = this.tokens.InsertLast(token); expression = new LiteralExpression(new CsTokenList(this.tokens, tokenNode, tokenNode), tokenNode); trueExpressionReference.Target = expression; break; default: throw new SyntaxException(sourceCode, symbol.LineNumber); } } // Gather up all extensions to this expression. while (expression != null) { Reference<ICodePart> expressionReference = new Reference<ICodePart>(expression); // Check if there is an extension to this expression. Expression extension = this.GetConditionalPreprocessorExpressionExtension(sourceCode, expressionReference, expression, previousPrecedence); if (extension != null) { // The larger expression is what we want to return here. expression = extension; } else { // There are no more extensions. break; } } // Return the expression. return expression; }
/// <summary> /// Checks the token text matches a ReSharper suppression. /// </summary> /// <param name="token"> /// The token to check. /// </param> /// <returns> /// True if its a ReSharper token otherwise false. /// </returns> public static bool IsAReSharperComment(CsToken token) { Param.AssertNotNull(token, "token"); if (token.CsTokenType != CsTokenType.MultiLineComment && token.CsTokenType != CsTokenType.SingleLineComment && token.CsTokenType != CsTokenType.XmlHeader && token.CsTokenType != CsTokenType.XmlHeaderLine) { return false; } string tokenText = token.Text; return tokenText.StartsWith("// ReSharper disable ") || tokenText.StartsWith("// ReSharper restore "); }
/// <summary> /// Checks to make sure that preprocessor type keyword is not preceded by a space. /// </summary> /// <param name="preprocessor"> /// The preprocessor token. /// </param> private void CheckPreprocessorSpacing(CsToken preprocessor) { Param.AssertNotNull(preprocessor, "preprocessor"); if (preprocessor.Text.Length > 1) { if (preprocessor.Text[0] == '#') { if (preprocessor.Text[1] == ' ' || preprocessor.Text[1] == '\t') { this.AddViolation(preprocessor.FindParentElement(), preprocessor.Location, Rules.PreprocessorKeywordsMustNotBePrecededBySpace); } } } }
private void CheckLineSpacingNonWhitespace( CsDocument document, Node<CsToken> precedingTokenNode, CsToken token, bool fileHeader, bool firstTokenOnLine, int count) { Param.AssertNotNull(document, "document"); Param.Ignore(precedingTokenNode); Param.AssertNotNull(token, "token"); Param.Ignore(fileHeader); Param.Ignore(firstTokenOnLine); Param.AssertGreaterThanOrEqualToZero(count, "count"); // Skip generated tokens. if (!token.Generated) { // If there is at least one blank line in front of the current token. if (count > 1) { if (token.CsTokenType == CsTokenType.CloseCurlyBracket) { // The blank line is just before a closing curly bracket. this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ClosingCurlyBracketsMustNotBePrecededByBlankLine); } else if (token.CsTokenType == CsTokenType.OpenCurlyBracket) { bool throwViolation = false; // The blank line is just before an opening curly bracket. // If next element back is if,while,catch,try,finally,do,else,lock,switch,unsafe, using, array init, delegate, it's a violation. if (precedingTokenNode == null) { throwViolation = true; } else { Statement parentStatement = precedingTokenNode.Value.FindParentStatement(); if (parentStatement == null) { throwViolation = true; } else { StatementType statementType = parentStatement.StatementType; if (statementType == StatementType.If || statementType == StatementType.While || statementType == StatementType.Catch || statementType == StatementType.Try || statementType == StatementType.Finally || statementType == StatementType.DoWhile || statementType == StatementType.Else || statementType == StatementType.Lock || statementType == StatementType.Switch || statementType == StatementType.Unsafe || statementType == StatementType.Using) { throwViolation = true; } else if (statementType == StatementType.VariableDeclaration && precedingTokenNode.Value.CsTokenType != CsTokenType.Semicolon) { throwViolation = true; } else if (statementType == StatementType.Expression && precedingTokenNode.Value.CsTokenType == CsTokenType.Delegate) { throwViolation = true; } } } if (throwViolation) { this.AddViolation(token.FindParentElement(), token.Location, Rules.OpeningCurlyBracketsMustNotBePrecededByBlankLine); } } else if (token.CsTokenType == CsTokenType.Else || token.CsTokenType == CsTokenType.Catch || token.CsTokenType == CsTokenType.Finally) { // The blank line is just before an else, catch, or finally statement. this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ChainedStatementBlocksMustNotBePrecededByBlankLine); } else if (token.CsTokenType == CsTokenType.WhileDo) { // The blank line is just before the while keyword from a do/while statement. this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.WhileDoFooterMustNotBePrecededByBlankLine); } // Check if there is a blank line after a single-line comment. The exceptions // are if this is the file header, or if the line after the blank line contains // another comment or an Xml header. This is ok if the comment is not // the first item on its line. if (!fileHeader && precedingTokenNode != null && precedingTokenNode.Value.CsTokenType == CsTokenType.SingleLineComment && token.CsTokenType != CsTokenType.SingleLineComment && token.CsTokenType != CsTokenType.MultiLineComment && token.CsTokenType != CsTokenType.XmlHeader) { // Now check whether the comment is the first item on its line. If the comment // is not the first item on the line, then this is not a violation. bool tokenSeen = false; if (precedingTokenNode != null) { foreach (CsToken lineToken in document.Tokens.ReverseIterator(precedingTokenNode.Previous)) { if (lineToken.CsTokenType == CsTokenType.EndOfLine) { break; } else if (lineToken.CsTokenType != CsTokenType.WhiteSpace) { tokenSeen = true; break; } } } // Now make sure this comment does not begin with '////'. If so, this is the signal // that StyleCop should ignore this particular error. This is used when the // developer is commenting out a line of code. In this case it is not a true comment. string trimmedComment = precedingTokenNode.Value.Text.Trim(); if (!tokenSeen && !trimmedComment.StartsWith(@"////", StringComparison.Ordinal)) { // The blank line appears after a file header, we want to allow this. if (!IsCommentInFileHeader(precedingTokenNode)) { this.AddViolation( precedingTokenNode.Value.FindParentElement(), precedingTokenNode.Value.LineNumber, Rules.SingleLineCommentsMustNotBeFollowedByBlankLine); } } } } else if (count == 1) { // There is one line return in front of the current token, which means // the line in front of the current token is not blank. Check if the current // token is an Xml header. if (token.CsTokenType == CsTokenType.XmlHeader) { // This is a violation unless the previous line contains another // Xml header, an opening curly bracket, or a preprocessor directive. if (precedingTokenNode != null && precedingTokenNode.Value.CsTokenType != CsTokenType.XmlHeader && precedingTokenNode.Value.CsTokenType != CsTokenType.OpenCurlyBracket && precedingTokenNode.Value.CsTokenType != CsTokenType.PreprocessorDirective) { this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.ElementDocumentationHeaderMustBePrecededByBlankLine); } } else if (token.CsTokenType == CsTokenType.SingleLineComment) { // The current line contains a single line comment and the previous line // is not blank. This is a violation unless the previous line contains // another single line comment, an opening curly bracket, a preprocessor // directive, or if the last character on the previous line is a colon, // which can only mean that it is a label or a case statement, in which // case this is ok. if (precedingTokenNode != null && precedingTokenNode.Value.CsTokenType != CsTokenType.SingleLineComment && precedingTokenNode.Value.CsTokenType != CsTokenType.OpenCurlyBracket && precedingTokenNode.Value.CsTokenType != CsTokenType.LabelColon && precedingTokenNode.Value.CsTokenType != CsTokenType.PreprocessorDirective) { // Now make sure this comment does not begin with '////'. If so, this is the signal // that StyleCop should ignore this particular error. This is used when the // developer is commenting out a line of code. In this case it is not a true comment. string trimmedComment = token.Text.Trim(); if (!trimmedComment.StartsWith(@"////", StringComparison.Ordinal) && !Utils.IsAReSharperComment(token)) { CsElement element = token.FindParentElement(); if (element != null) { this.AddViolation(element, token.LineNumber, Rules.SingleLineCommentMustBePrecededByBlankLine); } } } } else if (precedingTokenNode != null && precedingTokenNode.Value.CsTokenType == CsTokenType.CloseCurlyBracket) { // Closing curly brackets cannot be followed directly by another bracket keyword. Bracket closingCurlyBracket = precedingTokenNode.Value as Bracket; if (closingCurlyBracket.MatchingBracket != null && closingCurlyBracket.MatchingBracket.LineNumber != closingCurlyBracket.LineNumber && firstTokenOnLine && token.CsTokenType != CsTokenType.CloseCurlyBracket && token.CsTokenType != CsTokenType.Finally && token.CsTokenType != CsTokenType.Catch && token.CsTokenType != CsTokenType.WhileDo && token.CsTokenType != CsTokenType.Else && token.CsTokenType != CsTokenType.PreprocessorDirective && token.CsTokenType != CsTokenType.Select && token.CsTokenType != CsTokenType.From && token.CsTokenType != CsTokenType.Let && token.CsTokenType != CsTokenType.OperatorSymbol && token.CsTokenType != CsTokenType.By) { this.AddViolation(closingCurlyBracket.FindParentElement(), closingCurlyBracket.LineNumber, Rules.ClosingCurlyBracketMustBeFollowedByBlankLine); } } } } }
/// <summary> /// Reads a generic token from the document. /// </summary> /// <param name="genericTokenReference"> /// A reference to the generic token. /// </param> /// <param name="unsafeCode"> /// Indicates whether the code is marked as unsafe. /// </param> /// <param name="startIndex"> /// The first index of the generic. /// </param> /// <param name="lastIndex"> /// Returns the last index of the generic. /// </param> /// <returns> /// Returns the generic token, or null if the symbol manager is not sitting on a generic. /// </returns> /// <remarks> /// This should only be called by GetGenericToken. /// </remarks> private GenericType GetGenericTokenAux(Reference<ICodePart> genericTokenReference, bool unsafeCode, int startIndex, out int lastIndex) { Param.AssertNotNull(genericTokenReference, "genericTokenReference"); Param.Ignore(unsafeCode); Param.AssertGreaterThanOrEqualToZero(startIndex, "startIndex"); lastIndex = -1; // Get the first symbol. This should be an unknown word type. Symbol firstSymbol = this.symbols.Peek(startIndex); Debug.Assert(firstSymbol != null && firstSymbol.SymbolType == SymbolType.Other, "Expected a text symbol"); // This will hold the generic type if we create one. GenericType generic = null; // Create a token for the name. CsToken name = new CsToken(firstSymbol.Text, CsTokenType.Other, firstSymbol.Location, genericTokenReference, this.symbols.Generated); // Get the argument list. This will return null if this is not a generic. MasterList<CsToken> genericArgumentTokens = this.GetGenericArgumentList(genericTokenReference, unsafeCode, name, startIndex + 1, out lastIndex); if (genericArgumentTokens != null) { generic = new GenericType( genericArgumentTokens, CsToken.JoinLocations(firstSymbol.Location, genericArgumentTokens.Last), genericTokenReference, this.symbols.Generated); Reference<ICodePart> genericTypeReference = new Reference<ICodePart>(generic); foreach (CsToken token in genericArgumentTokens) { token.ParentRef = genericTypeReference; } } return generic; }
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> /// Determines whether the given xml header is empty. /// </summary> /// <param name="token"> /// The xml header line token. /// </param> /// <returns> /// Returns true if the header is empty. /// </returns> private static bool IsXmlHeaderLineEmpty(CsToken token) { Param.AssertNotNull(token, "token"); Debug.Assert(token.CsTokenType == CsTokenType.XmlHeaderLine, "The token should be an xml header line"); int slashCount = 0; for (int i = 0; i < token.Text.Length; ++i) { char character = token.Text[i]; if (slashCount < 3) { if (character == '/') { ++slashCount; } else { return false; } } else { if (!char.IsWhiteSpace(character)) { return false; } } } return true; }
/// <summary> /// Determines whether a matching local variable is contained in the given variable list. /// </summary> /// <param name="variables"> /// The variable list. /// </param> /// <param name="word"> /// The variable name to check. /// </param> /// <param name="item"> /// The token containing the variable name. /// </param> /// <returns> /// Returns true if there is a matching local variable. /// </returns> private static bool ContainsVariable(VariableCollection variables, string word, CsToken item) { Param.AssertNotNull(variables, "variables"); Param.AssertValidString(word, "word"); Param.AssertNotNull(item, "item"); word = word.SubstringAfter('@'); Variable variable = variables[word]; if (variable != null) { // Make sure the variable appears before the word. if (variable.Location.LineNumber < item.LineNumber) { return(true); } else if (variable.Location.LineNumber == item.LineNumber) { if (variable.Location.StartPoint.IndexOnLine < item.Location.StartPoint.IndexOnLine) { return(true); } } } return(false); }
/// <summary> /// Determines whether a matching local variable is contained in the given variable list. /// </summary> /// <param name="variables"> /// The variable list. /// </param> /// <param name="word"> /// The variable name to check. /// </param> /// <param name="item"> /// The token containing the variable name. /// </param> /// <returns> /// Returns true if there is a matching local variable. /// </returns> private static bool ContainsVariable(VariableCollection variables, string word, CsToken item) { Param.AssertNotNull(variables, "variables"); Param.AssertValidString(word, "word"); Param.AssertNotNull(item, "item"); word = word.SubstringAfter('@'); Variable variable = variables[word]; if (variable != null) { // Make sure the variable appears before the word. if (variable.Location.LineNumber < item.LineNumber) { return true; } else if (variable.Location.LineNumber == item.LineNumber) { if (variable.Location.StartPoint.IndexOnLine < item.Location.StartPoint.IndexOnLine) { return true; } } } return false; }
/// <summary> /// Compares the token to the Operator symbol and to a dot '.'. /// </summary> /// <param name="token"> /// The token to check. /// </param> /// <returns> /// True is the token is an operator and a dot '.', otherwise false. /// </returns> private static bool IsTokenADot(CsToken token) { Param.AssertNotNull(token, "token"); if (token.CsTokenType == CsTokenType.OperatorSymbol) { OperatorSymbol symbol = (OperatorSymbol)token; if (symbol.SymbolType == OperatorType.MemberAccess) { return symbol.Text == "."; } } return false; }
/// <summary> /// Checks for tabs in the given comment. /// </summary> /// <param name="comment"> /// The comment token. /// </param> private void CheckTabsInComment(CsToken comment) { Param.AssertNotNull(comment, "comment"); int lineEnds = 0; for (int i = 0; i < comment.Text.Length; ++i) { if (comment.Text[i] == '\t') { this.AddViolation(comment.FindParentElement(), comment.Location, Rules.TabsMustNotBeUsed); } else if (comment.Text[i] == '\n') { ++lineEnds; } } }
/// <summary> /// Joins the locations of the two tokens. /// </summary> /// <param name="location1"> /// The first location. /// </param> /// <param name="token2"> /// The second token. /// </param> /// <returns> /// Returns the joined locations. /// </returns> internal static CodeLocation JoinLocations(CodeLocation location1, CsToken token2) { Param.Ignore(location1, token2); return token2 == null ? CodeLocation.Join(location1, null) : CodeLocation.Join(location1, token2.Location); }
/////// <summary> /////// Joins the locations of the two tokens. /////// </summary> /////// <param name="token1">The first token.</param> /////// <param name="location2">The second location.</param> /////// <returns>Returns the joined locations.</returns> ////internal static CodeLocation JoinLocations(Node<CsToken> token1, CodeLocation location2) ////{ //// Param.Ignore(token1, location2); //// return CsToken.JoinLocations(token1 == null ? null : token1.Value, location2); ////} /// <summary> /// Joins the locations of the two tokens. /// </summary> /// <param name="token1"> /// The first token. /// </param> /// <param name="token2"> /// The second token. /// </param> /// <returns> /// Returns the joined locations. /// </returns> internal static CodeLocation JoinLocations(CsToken token1, CsToken token2) { Param.AssertNotNull(token1, "token1"); Param.AssertNotNull(token2, "token2"); return CodeLocation.Join(token1.Location, token2.Location); }
/// <summary> /// Reads an expression starting with an unknown word. /// </summary> /// <returns>Returns the expression.</returns> private LiteralExpression GetConditionalPreprocessorConstantExpression() { // Get the first symbol. Symbol symbol = this.symbols.Peek(1); Debug.Assert(symbol != null && symbol.SymbolType == SymbolType.Other, "Expected a text symbol"); Reference<ICodePart> expressionReference = new Reference<ICodePart>(); // Convert the symbol to a token. this.symbols.Advance(); CsToken literalToken = new CsToken(symbol.Text, CsTokenType.Other, symbol.Location, expressionReference, this.symbols.Generated); Node<CsToken> literalTokenNode = this.tokens.InsertLast(literalToken); // Create a literal expression from this token. LiteralExpression expression = new LiteralExpression(this.tokens, literalTokenNode); expressionReference.Target = expression; return expression; }
/// <summary> /// Reads the next expression from a conditional preprocessor directive. /// </summary> /// <param name="sourceCode"> /// The source code. /// </param> /// <param name="previousPrecedence"> /// The precedence of the expression just before this one. /// </param> /// <returns> /// Returns the expression. /// </returns> private Expression GetNextConditionalPreprocessorExpression(SourceCode sourceCode, ExpressionPrecedence previousPrecedence) { Param.AssertNotNull(sourceCode, "sourceCode"); Param.Ignore(previousPrecedence); Reference <ICodePart> parentReference = new Reference <ICodePart>(); // Move past comments and whitepace. this.AdvanceToNextConditionalDirectiveCodeSymbol(parentReference); // Saves the next expression. Expression expression = null; // Get the next symbol. Symbol symbol = this.symbols.Peek(1); if (symbol != null) { switch (symbol.SymbolType) { case SymbolType.Other: expression = this.GetConditionalPreprocessorConstantExpression(); break; case SymbolType.Not: expression = this.GetConditionalPreprocessorNotExpression(sourceCode, parentReference); break; case SymbolType.OpenParenthesis: expression = this.GetConditionalPreprocessorParenthesizedExpression(sourceCode, parentReference); break; case SymbolType.False: this.symbols.Advance(); Reference <ICodePart> falseExpressionReference = new Reference <ICodePart>(); CsToken token = new CsToken(symbol.Text, CsTokenType.False, symbol.Location, falseExpressionReference, this.symbols.Generated); Node <CsToken> tokenNode = this.tokens.InsertLast(token); expression = new LiteralExpression(new CsTokenList(this.tokens, tokenNode, tokenNode), tokenNode); falseExpressionReference.Target = expression; break; case SymbolType.True: this.symbols.Advance(); Reference <ICodePart> trueExpressionReference = new Reference <ICodePart>(); token = new CsToken(symbol.Text, CsTokenType.True, symbol.Location, trueExpressionReference, this.symbols.Generated); tokenNode = this.tokens.InsertLast(token); expression = new LiteralExpression(new CsTokenList(this.tokens, tokenNode, tokenNode), tokenNode); trueExpressionReference.Target = expression; break; default: throw new SyntaxException(sourceCode, symbol.LineNumber); } } // Gather up all extensions to this expression. while (expression != null) { Reference <ICodePart> expressionReference = new Reference <ICodePart>(expression); // Check if there is an extension to this expression. Expression extension = this.GetConditionalPreprocessorExpressionExtension(sourceCode, expressionReference, expression, previousPrecedence); if (extension != null) { // The larger expression is what we want to return here. expression = extension; } else { // There are no more extensions. break; } } // Return the expression. return(expression); }
/// <summary> /// Returns the net count of opening and closing 'code' and 'c' elements for the token provided. /// </summary> /// <param name="token"> /// The xml header line token. /// </param> /// <returns> /// The net count of open and close 'code' and 'c' elements. /// </returns> private static int XmlHeaderLineCodeElementCount(CsToken token) { Param.AssertNotNull(token, "token"); Debug.Assert(token.CsTokenType == CsTokenType.XmlHeaderLine, "The token should be an xml header line"); string lineText = token.Text; // Search for '<code ' without the closing tag because sometimes users have added attributes like <code lang="C#"> and <code> too int openCodeTagCount = CountOfStringInStringOccurrences(lineText, "<code ", "<code>", "<c>"); int closeCodeTagCount = CountOfStringInStringOccurrences(lineText, "</code>", "</c>"); return openCodeTagCount - closeCodeTagCount; }
/// <summary> /// Joins the locations of the two tokens. /// </summary> /// <param name="location1"> /// The first location. /// </param> /// <param name="token2"> /// The second token. /// </param> /// <returns> /// Returns the joined locations. /// </returns> internal static CodeLocation JoinLocations(CodeLocation location1, CsToken token2) { Param.Ignore(location1, token2); return(token2 == null?CodeLocation.Join(location1, null) : CodeLocation.Join(location1, token2.Location)); }
private CsToken ConvertOperatorOverloadSymbol(Reference<ICodePart> parentReference) { Param.AssertNotNull(parentReference, "parentReference"); CsToken token = null; Symbol symbol = this.symbols.Peek(1); if (symbol != null) { if (symbol.SymbolType == SymbolType.GreaterThan) { Symbol next = this.symbols.Peek(2); if (next != null && next.SymbolType == SymbolType.GreaterThan) { // This could be a right-shift-equals. next = this.symbols.Peek(3); if (next != null && next.SymbolType == SymbolType.Equals) { // This is a right-shift-equals. this.symbols.Combine(1, 3, ">>=", SymbolType.RightShiftEquals); } else { // This is a right-shift. this.symbols.Combine(1, 2, ">>", SymbolType.RightShift); } } symbol = this.symbols.Peek(1); token = new CsToken(symbol.Text, CsTokenType.Other, symbol.Location, parentReference, this.symbols.Generated); this.symbols.Advance(); } else { token = new CsToken(symbol.Text, CsTokenType.Other, symbol.Location, parentReference, this.symbols.Generated); this.symbols.Advance(); } } return token; }
/////// <summary> /////// Joins the locations of the two tokens. /////// </summary> /////// <param name="token1">The first token.</param> /////// <param name="token2">The second token.</param> /////// <returns>Returns the joined locations.</returns> ////internal static CodeLocation JoinLocations(Node<CsToken> token1, CsToken token2) ////{ //// Param.Ignore(token1, token2); //// return CsToken.JoinLocations(token1 == null ? null : token1.Value, token2); ////} /////// <summary> /////// Joins the locations of the two tokens. /////// </summary> /////// <param name="token1">The first token.</param> /////// <param name="token2">The second token.</param> /////// <returns>Returns the joined locations.</returns> ////internal static CodeLocation JoinLocations(CsToken token1, Node<CsToken> token2) ////{ //// Param.Ignore(token1, token2); //// return CsToken.JoinLocations(token1, token2 == null ? null : token2.Value); ////} /// <summary> /// Joins the locations of the two tokens. /// </summary> /// <param name="token1"> /// The first token. /// </param> /// <param name="token2"> /// The second token. /// </param> /// <returns> /// Returns the joined locations. /// </returns> internal static CodeLocation JoinLocations(Node <CsToken> token1, Node <CsToken> token2) { Param.Ignore(token1, token2); return(CsToken.JoinLocations(token1 == null ? null : token1.Value, token2 == null ? null : token2.Value)); }
/// <summary> /// Initializes a new instance of the Argument class. /// </summary> /// <param name="name"> /// The optional name of the argument. /// </param> /// <param name="modifiers"> /// Modifiers applied to this argument. /// </param> /// <param name="argumentExpression"> /// The expression that forms the body of the argument. /// </param> /// <param name="location"> /// The location of the argument in the code. /// </param> /// <param name="parent"> /// The parent code part. /// </param> /// <param name="tokens"> /// The tokens that form the argument. /// </param> /// <param name="generated"> /// Indicates whether the argument is located within a block of generated code. /// </param> internal Argument( CsToken name, ParameterModifiers modifiers, Expression argumentExpression, CodeLocation location, Reference<ICodePart> parent, CsTokenList tokens, bool generated) { Param.Ignore(name); Param.Ignore(modifiers); Param.AssertNotNull(argumentExpression, "argumentExpression"); Param.AssertNotNull(location, "location"); Param.AssertNotNull(parent, "parent"); Param.Ignore(tokens); Param.Ignore(generated); this.name = name; this.modifiers = modifiers; this.argumentExpression = argumentExpression; this.location = location; this.parent = parent; this.tokens = tokens; this.generated = generated; }
private MasterList<CsToken> GetGenericArgumentList(Reference<ICodePart> genericTypeReference, bool unsafeCode, CsToken name, int startIndex, out int endIndex) { Param.AssertNotNull(genericTypeReference, "genericTypeReference"); Param.Ignore(unsafeCode); Param.Ignore(name); Param.AssertGreaterThanOrEqualToZero(startIndex, "startIndex"); endIndex = -1; MasterList<CsToken> genericArgumentListTokens = null; // Move past whitespace and comments. int index = startIndex; while (true) { Symbol next = this.symbols.Peek(index); if (next == null || (next.SymbolType != SymbolType.WhiteSpace && next.SymbolType != SymbolType.EndOfLine && next.SymbolType != SymbolType.SingleLineComment && next.SymbolType != SymbolType.MultiLineComment && next.SymbolType != SymbolType.PreprocessorDirective)) { break; } ++index; } // The next symbol should be an opening bracket, if this is a generic. Symbol symbol = this.symbols.Peek(index); if (symbol != null && symbol.SymbolType == SymbolType.LessThan) { // This might be a generic. Assume that it is and start creating tokens. genericArgumentListTokens = new MasterList<CsToken>(); // Add the name if one was provided. if (name != null) { genericArgumentListTokens.Add(name); } Node<CsToken> openingGenericBracketNode = null; // Add everything up to the opening bracket into the token list. for (int i = startIndex; i <= index; ++i) { symbol = this.symbols.Peek(i); Debug.Assert(symbol != null, "The next symbol should not be null"); if (symbol.SymbolType == SymbolType.LessThan) { if (openingGenericBracketNode != null) { // This is not a generic statement. return null; } Bracket openingGenericBracket = new Bracket( symbol.Text, CsTokenType.OpenGenericBracket, symbol.Location, genericTypeReference, this.symbols.Generated); openingGenericBracketNode = genericArgumentListTokens.InsertLast(openingGenericBracket); } else { genericArgumentListTokens.Add(this.ConvertSymbol(symbol, TokenTypeFromSymbolType(symbol.SymbolType), genericTypeReference)); } } // Loop through the rest of the symbols. while (true) { symbol = this.symbols.Peek(++index); if (symbol == null) { // The code ran out before we found the end of the generic. throw new SyntaxException(this.document.SourceCode, name.LineNumber); } else if (symbol.SymbolType == SymbolType.GreaterThan) { if (openingGenericBracketNode == null) { // This is not a generic statement. return null; } // This is the end of the generic statement. Add the closing bracket to the token list. Bracket closingGenericBracket = new Bracket( symbol.Text, CsTokenType.CloseGenericBracket, symbol.Location, genericTypeReference, this.symbols.Generated); Node<CsToken> closingGenericBracketNode = genericArgumentListTokens.InsertLast(closingGenericBracket); ((Bracket)openingGenericBracketNode.Value).MatchingBracketNode = closingGenericBracketNode; closingGenericBracket.MatchingBracketNode = openingGenericBracketNode; endIndex = index; break; } else if (symbol.SymbolType == SymbolType.Out || symbol.SymbolType == SymbolType.In) { // Get the in or out keyword. genericArgumentListTokens.Add( this.ConvertSymbol(symbol, symbol.SymbolType == SymbolType.In ? CsTokenType.In : CsTokenType.Out, genericTypeReference)); } else if (symbol.SymbolType == SymbolType.Other) { int lastIndex = 0; Reference<ICodePart> wordReference = new Reference<ICodePart>(); CsToken word = this.GetTypeTokenAux(wordReference, genericTypeReference, unsafeCode, true, false, index, out lastIndex); if (word == null) { throw new SyntaxException(this.document.SourceCode, symbol.LineNumber); } // Advance the index to the end of the token. index = lastIndex; // Add the token. genericArgumentListTokens.Add(word); } else if (symbol.SymbolType == SymbolType.WhiteSpace || symbol.SymbolType == SymbolType.EndOfLine || symbol.SymbolType == SymbolType.SingleLineComment || symbol.SymbolType == SymbolType.MultiLineComment || symbol.SymbolType == SymbolType.PreprocessorDirective) { // Add these to the token list. genericArgumentListTokens.Add(this.ConvertSymbol(symbol, TokenTypeFromSymbolType(symbol.SymbolType), genericTypeReference)); } else if (symbol.SymbolType == SymbolType.Comma) { genericArgumentListTokens.Add(this.ConvertSymbol(symbol, CsTokenType.Comma, genericTypeReference)); } else if (symbol.SymbolType == SymbolType.OpenSquareBracket) { // An attribute on the generic type. genericArgumentListTokens.Add(this.GetAttribute(genericTypeReference, unsafeCode)); } else { // Any other symbol signifies that this is not a generic statement. genericArgumentListTokens = null; break; } } } return genericArgumentListTokens; }
/// <summary> /// Reads the next variable declaration statement from the file and returns it. /// </summary> /// <param name="parentReference"> /// The parent code unit. /// </param> /// <param name="unsafeCode"> /// Indicates whether the code being parsed resides in an unsafe code block. /// </param> /// <param name="variables"> /// Returns the list of variables defined in the statement. /// </param> /// <returns> /// Returns the statement. /// </returns> private VariableDeclarationStatement ParseVariableDeclarationStatement(Reference<ICodePart> parentReference, bool unsafeCode, VariableCollection variables) { Param.AssertNotNull(parentReference, "parentReference"); Param.Ignore(unsafeCode); Param.Ignore(variables); bool constant = false; // Get the first symbol and make sure it is an unknown word or a const. Symbol symbol = this.GetNextSymbol(parentReference); CsToken firstToken = null; Node<CsToken> firstTokenNode = null; Reference<ICodePart> statementReference = new Reference<ICodePart>(); if (symbol.SymbolType == SymbolType.Const) { constant = true; firstToken = new CsToken(symbol.Text, CsTokenType.Const, symbol.Location, statementReference, this.symbols.Generated); firstTokenNode = this.tokens.InsertLast(this.GetToken(CsTokenType.Const, SymbolType.Const, statementReference)); symbol = this.GetNextSymbol(statementReference); } if (symbol.SymbolType != SymbolType.Other) { throw this.CreateSyntaxException(); } // Get the expression representing the type. LiteralExpression type = this.GetTypeTokenExpression(statementReference, unsafeCode, true); if (type == null || type.Tokens.First == null) { throw new SyntaxException(this.document.SourceCode, firstToken.LineNumber); } if (firstTokenNode == null) { firstTokenNode = type.Tokens.First; } // Get the rest of the declaration. VariableDeclarationExpression expression = this.GetVariableDeclarationExpression(type, ExpressionPrecedence.None, unsafeCode); // Get the closing semicolon. this.tokens.Add(this.GetToken(CsTokenType.Semicolon, SymbolType.Semicolon, statementReference)); // Add each of the variables defined in this statement to the variable list being returned. if (variables != null) { VariableModifiers modifiers = constant ? VariableModifiers.Const : VariableModifiers.None; foreach (VariableDeclaratorExpression declarator in expression.Declarators) { Variable variable = new Variable( expression.Type, declarator.Identifier.Token.Text, modifiers, CodeLocation.Join(expression.Type.Location, declarator.Identifier.Token.Location), statementReference, expression.Tokens.First.Value.Generated || declarator.Identifier.Token.Generated); // There might already be a variable in this scope with the same name. This can happen // in valid situation when there are ifdef's surrounding portions of the code. // Just accept the first variable and ignore others. if (!variables.Contains(declarator.Identifier.Token.Text)) { variables.Add(variable); } } } // Create the token list for the statement. CsTokenList partialTokens = new CsTokenList(this.tokens, firstTokenNode, this.tokens.Last); VariableDeclarationStatement statement = new VariableDeclarationStatement(partialTokens, constant, expression); statementReference.Target = statement; return statement; }
/// <summary> /// Gets the next variable. /// </summary> /// <param name="parentReference"> /// The parent code unit. /// </param> /// <param name="unsafeCode"> /// Indicates whether the code is within an unsafe block. /// </param> /// <param name="allowTypelessVariable"> /// Indicates whether to allow a variable with no type defined. /// </param> /// <param name="onlyTypelessVariable"> /// Indicates whether to only get a typeless variable. /// </param> /// <returns> /// Returns the variable. /// </returns> private Variable GetVariable(Reference<ICodePart> parentReference, bool unsafeCode, bool allowTypelessVariable, bool onlyTypelessVariable) { Param.AssertNotNull(parentReference, "parentReference"); Param.Ignore(unsafeCode); Param.Ignore(allowTypelessVariable); Param.Ignore(onlyTypelessVariable); this.AdvanceToNextCodeSymbol(parentReference); Reference<ICodePart> variableReference = new Reference<ICodePart>(); // Get the type token representing either the type or the identifier. TypeToken type = this.GetTypeToken(variableReference, unsafeCode, true, false); if (type == null) { throw this.CreateSyntaxException(); } Variable variable = null; if (onlyTypelessVariable) { // The token is not a type, just an identifier. Debug.Assert(type.ChildTokens.Count == 1, "The count is invalid"); CsToken identifierToken = type.ChildTokens.First.Value; this.tokens.Add(identifierToken); variable = new Variable(null, type.Text, VariableModifiers.None, type.Location, parentReference, type.Generated); identifierToken.ParentRef = new Reference<ICodePart>(variable); } else { int index = this.GetNextCodeSymbolIndex(1); if (index != -1) { // Look ahead to the next symbol to see what it is. Symbol symbol = this.symbols.Peek(index); if (symbol == null || symbol.SymbolType != SymbolType.Other) { // This variable has no type, only an identifier. if (!allowTypelessVariable) { throw this.CreateSyntaxException(); } // The token is not a type, just an identifier. Debug.Assert(type.ChildTokens.Count == 1, "The count is invalid"); this.tokens.Add(type.ChildTokens.First.Value); variable = new Variable(null, type.Text, VariableModifiers.None, type.Location, parentReference, type.Generated); } else { // There is a type so add the type token. this.tokens.Add(type); this.AdvanceToNextCodeSymbol(variableReference); // Create and add the identifier token. CsToken identifier = new CsToken(symbol.Text, CsTokenType.Other, CsTokenClass.Token, symbol.Location, variableReference, this.symbols.Generated); this.tokens.Add(identifier); this.symbols.Advance(); // The variable has both a type and an identifier. variable = new Variable( type, identifier.Text, VariableModifiers.None, CodeLocation.Join(type.Location, identifier.Location), parentReference, type.Generated || identifier.Generated); } } } variableReference.Target = variable; return variable; }
/// <summary> /// Visits the token. /// </summary> /// <param name="document">The document.</param> /// <param name="token">The token.</param> public virtual void VisitToken(CodeDocument document, CsToken token) { }