/// <summary> /// Initializes a new instance of the <see cref="CodeWalker{T}"/> class. /// Initializes a new instance of the CodeWalker class. /// </summary> /// <param name="queryClause"> /// The query clause to walk through. /// </param> /// <param name="statementCallback"> /// Callback executed when a statement is visited. /// </param> /// <param name="expressionCallback"> /// Callback executed when an expression is visited. /// </param> /// <param name="queryClauseCallback"> /// Callback executed when a query clause is visited. /// </param> /// <param name="context"> /// The optional visitor context data. /// </param> private CodeWalker( QueryClause queryClause, CodeWalkerStatementVisitor <T> statementCallback, CodeWalkerExpressionVisitor <T> expressionCallback, CodeWalkerQueryClauseVisitor <T> queryClauseCallback, T context) { Param.AssertNotNull(queryClause, "queryClause"); Param.Ignore(statementCallback); Param.Ignore(expressionCallback); Param.Ignore(queryClauseCallback); Param.Ignore(context); this.statementCallback = statementCallback; this.expressionCallback = expressionCallback; this.queryClauseCallback = queryClauseCallback; this.WalkQueryClause( queryClause, queryClause.ParentQueryClause, queryClause.FindParentExpression(), queryClause.FindParentStatement(), queryClause.FindParentElement(), context); }
/// <summary> /// Processes the given query expression. /// </summary> /// <param name="element"> /// The element that contains the expression. /// </param> /// <param name="queryExpression"> /// The query expression. /// </param> private void CheckQueryExpression(CsElement element, QueryExpression queryExpression) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(queryExpression, "queryExpression"); QueryClause previousClause = null; bool clauseOnSameLine = false; bool clauseOnSeparateLine = false; this.ProcessQueryClauses(element, queryExpression, queryExpression.ChildClauses, ref previousClause, ref clauseOnSameLine, ref clauseOnSeparateLine); }
public static void Start( QueryClause queryClause, CodeWalkerStatementVisitor <T> statementCallback, CodeWalkerExpressionVisitor <T> expressionCallback, CodeWalkerQueryClauseVisitor <T> queryClauseCallback, T context) { Param.AssertNotNull(queryClause, "queryClause"); Param.Ignore(statementCallback); Param.Ignore(expressionCallback); Param.Ignore(queryClauseCallback); Param.Ignore(context); new CodeWalker <T>(queryClause, statementCallback, expressionCallback, queryClauseCallback, context); }
/// <summary> /// Delegate for a callback executed when a query clause is visited. /// </summary> /// <param name="clause"> /// The query clause being visited. /// </param> /// <param name="parentClause"> /// The parent query clause, if any. /// </param> /// <param name="parentExpression"> /// The parent expression, if any. /// </param> /// <param name="parentStatement"> /// The parent statement, if any. /// </param> /// <param name="parentElement"> /// The parent element, if any. /// </param> /// <param name="context"> /// The optional visitor context data. /// </param> /// <returns> /// Returns true to continue, or false to stop the walker. /// </returns> private bool VisitQueryClause( QueryClause clause, QueryClause parentClause, Expression parentExpression, Statement parentStatement, CsElement parentElement, ref T context) { Param.AssertNotNull(clause, "clause"); Param.Ignore(parentClause); Param.Ignore(parentExpression); Param.Ignore(parentStatement); Param.Ignore(parentElement); Param.Ignore(context); if (this.queryClauseCallback != null) { return(this.queryClauseCallback(clause, parentClause, parentExpression, parentStatement, parentElement, context)); } return(true); }
/// <summary> /// Walks the children of the given query clause. /// </summary> /// <param name="clause"> /// The clause. /// </param> /// <param name="parentClause"> /// The parent clause, if any. /// </param> /// <param name="parentExpression"> /// The parent expression, if any. /// </param> /// <param name="parentStatement"> /// The parent statement, if any. /// </param> /// <param name="parentElement"> /// The parent element, if any. /// </param> /// <param name="context"> /// The optional visitor context data. /// </param> /// <returns> /// Returns true to continue, or false to stop the walker. /// </returns> private bool WalkQueryClause( QueryClause clause, QueryClause parentClause, Expression parentExpression, Statement parentStatement, CsElement parentElement, T context) { Param.Ignore(clause, parentClause, parentExpression, parentStatement, parentElement, context); if (clause != null) { T childContext = context; if (!this.VisitQueryClause(clause, parentClause, parentExpression, parentStatement, parentElement, ref childContext)) { return(false); } foreach (Expression childExpression in clause.ChildExpressions) { if (!this.WalkExpression(childExpression, parentExpression, parentStatement, parentElement, childContext)) { return(false); } } if (clause.QueryClauseType == QueryClauseType.Continuation) { QueryContinuationClause continuationClause = (QueryContinuationClause)clause; foreach (QueryClause childClause in continuationClause.ChildClauses) { if (!this.WalkQueryClause(childClause, clause, parentExpression, parentStatement, parentElement, childContext)) { return(false); } } } } return(true); }
/// <summary> /// Analyzes the given query clauses. /// </summary> /// <param name="element"> /// The element containing the clauses. /// </param> /// <param name="expression"> /// The expression containing the clauses. /// </param> /// <param name="clauses"> /// The list of clauses to analyze. /// </param> /// <param name="previousClause"> /// The previous clause in the expression, if any. /// </param> /// <param name="clauseOnSameLine"> /// Indicates whether any clause has been seen previously which /// starts on the same line as the clause before it. /// </param> /// <param name="clauseOnSeparateLine"> /// Indicates whether any clause has been seen previously which /// starts on the line after the clause before it. /// </param> /// <returns> /// Returns true to continue checking the query clause, or false to quit. /// </returns> private bool ProcessQueryClauses( CsElement element, QueryExpression expression, ICollection <QueryClause> clauses, ref QueryClause previousClause, ref bool clauseOnSameLine, ref bool clauseOnSeparateLine) { Param.AssertNotNull(element, "element"); Param.AssertNotNull(expression, "expression"); Param.AssertNotNull(clauses, "clauses"); Param.Ignore(previousClause); Param.Ignore(clauseOnSameLine); Param.Ignore(clauseOnSeparateLine); foreach (QueryClause clause in clauses) { if (previousClause != null) { // Figure out the line number that the previous clause ends on. For most // clauses, this is simply the end point of the clause location property, // but for continuation clauses we want to use the location of the 'into' variable, // which conceptually represents the end of the continuation line. int previousClauseEndLineNumber = previousClause.Location.EndPoint.LineNumber; if (previousClause.QueryClauseType == QueryClauseType.Continuation) { previousClauseEndLineNumber = ((QueryContinuationClause)previousClause).Variable.Location.LineNumber; } // Ensure that the clause either starts on the same line as the expression, or // on the very next line. if (clause.LineNumber == previousClauseEndLineNumber) { // This is only ok if the previous clause does not span multiple lines. if (previousClause.Location.LineSpan > 1) { this.AddViolation(element, clause.LineNumber, Rules.QueryClauseMustBeginOnNewLineWhenPreviousClauseSpansMultipleLines); return(false); } // The rest of the checks are only applied when the clause is not a query continuation clause. A continuation // clause is allowed to begin at the end of the previous clause, on the same line. if (clause.QueryClauseType != QueryClauseType.Continuation) { // The clause starts on the same line as the ending of the previous clause. // This is ok as long as we have not previously seen a clause which starts // on its own line. The one exception is that query continuation clauses // are allowed to be inserted at the end of the previous claus. if (clauseOnSeparateLine) { this.AddViolation(element, clause.LineNumber, Rules.QueryClausesMustBeOnSeparateLinesOrAllOnOneLine); return(false); } // If the clause spans multiple lines, it must begin on its own line. The exception is query continuation // clauses, which are allowed to begin at the end of the previous claus. if (clause.Location.LineSpan > 1) { this.AddViolation(element, clause.LineNumber, Rules.QueryClausesSpanningMultipleLinesMustBeginOnOwnLine); return(false); } // Indicate that we have seen a clause which starts on the same line as the // previous clause. clauseOnSameLine = true; } } else if (clause.LineNumber == previousClauseEndLineNumber + 1) { // The clause starts on the line just after the previous clause. // This is fine unless we have previously seen two clauses on the same line. if (clauseOnSameLine) { this.AddViolation(element, clause.LineNumber, Rules.QueryClausesMustBeOnSeparateLinesOrAllOnOneLine); return(false); } // Indicate that we have seen a clause which begins on the line after // the previous clause. clauseOnSeparateLine = true; } else if (clause.LineNumber > previousClauseEndLineNumber + 1) { // The clause does not start on the line after the previous clause. this.AddViolation(element, clause.LineNumber, Rules.QueryClauseMustFollowPreviousClause); return(false); } } previousClause = clause; if (clause.QueryClauseType == QueryClauseType.Continuation) { QueryContinuationClause continuationClause = (QueryContinuationClause)clause; if ( !this.ProcessQueryClauses( element, expression, continuationClause.ChildClauses, ref previousClause, ref clauseOnSameLine, ref clauseOnSeparateLine)) { return(false); } } } return(true); }