Example #1
0
        /// <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);
        }
Example #3
0
        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);
        }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <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);
        }