/// <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);
        }
        /// <summary>
        /// Analyzes the given query clauses.
        /// </summary>
        /// <param name="element">The element containing the clauses.</param>
        /// <param name="expression">The expression containing the clauses.</param>
        /// <param name="clauses">The list of clauses to analyze.</param>
        /// <param name="previousClause">The previous clause in the expression, if any.</param>
        /// <param name="clauseOnSameLine">Indicates whether any clause has been seen previously which
        /// starts on the same line as the clause before it.</param>
        /// <param name="clauseOnSeparateLine">Indicates whether any clause has been seen previously which
        /// starts on the line after the clause before it.</param>
        /// <returns>Returns true to continue checking the query clause, or false to quit.</returns>
        private bool ProcessQueryClauses(
            CsElement element,
            QueryExpression expression,
            ICollection<QueryClause> clauses,
            ref QueryClause previousClause,
            ref bool clauseOnSameLine,
            ref bool clauseOnSeparateLine)
        {
            Param.AssertNotNull(element, "element");
            Param.AssertNotNull(expression, "expression");
            Param.AssertNotNull(clauses, "clauses");
            Param.Ignore(previousClause);
            Param.Ignore(clauseOnSameLine);
            Param.Ignore(clauseOnSeparateLine);

            foreach (QueryClause clause in clauses)
            {
                if (previousClause != null)
                {
                    // Figure out the line number that the previous clause ends on. For most
                    // clauses, this is simply the end point of the clause location property,
                    // but for continuation clauses we want to use the location of the 'into' variable,
                    // which conceptually represents the end of the continuation line.
                    int previousClauseEndLineNumber = previousClause.Location.EndPoint.LineNumber;

                    if (previousClause.QueryClauseType == QueryClauseType.Continuation)
                    {
                        previousClauseEndLineNumber = ((QueryContinuationClause)previousClause).Variable.Location.LineNumber;
                    }

                    // Ensure that the clause either starts on the same line as the expression, or
                    // on the very next line.
                    if (clause.LineNumber == previousClauseEndLineNumber)
                    {
                        // This is only ok if the previous clause does not span multiple lines.
                        if (previousClause.Location.LineSpan > 1)
                        {
                            this.AddViolation(element, clause.LineNumber, Rules.QueryClauseMustBeginOnNewLineWhenPreviousClauseSpansMultipleLines);
                            return false;
                        }

                        // The rest of the checks are only applied when the clause is not a query continuation clause. A continuation
                        // clause is allowed to begin at the end of the previous clause, on the same line.
                        if (clause.QueryClauseType != QueryClauseType.Continuation)
                        {
                            // The clause starts on the same line as the ending of the previous clause.
                            // This is ok as long as we have not previously seen a clause which starts
                            // on its own line. The one exception is that query continuation clauses
                            // are allowed to be inserted at the end of the previous claus.
                            if (clauseOnSeparateLine)
                            {
                                this.AddViolation(element, clause.LineNumber, Rules.QueryClausesMustBeOnSeparateLinesOrAllOnOneLine);
                                return false;
                            }

                            // If the clause spans multiple lines, it must begin on its own line. The exception is query continuation
                            // clauses, which are allowed to begin at the end of the previous claus.
                            if (clause.Location.LineSpan > 1)
                            {
                                this.AddViolation(element, clause.LineNumber, Rules.QueryClausesSpanningMultipleLinesMustBeginOnOwnLine);
                                return false;
                            }

                            // Indicate that we have seen a clause which starts on the same line as the
                            // previous clause.
                            clauseOnSameLine = true;
                        }
                    }
                    else if (clause.LineNumber == previousClauseEndLineNumber + 1)
                    {
                        // The clause starts on the line just after the previous clause.
                        // This is fine unless we have previously seen two clauses on the same line.
                        if (clauseOnSameLine)
                        {
                            this.AddViolation(element, clause.LineNumber, Rules.QueryClausesMustBeOnSeparateLinesOrAllOnOneLine);
                            return false;
                        }

                        // Indicate that we have seen a clause which begins on the line after
                        // the previous clause.
                        clauseOnSeparateLine = true;
                    }
                    else if (clause.LineNumber > previousClauseEndLineNumber + 1)
                    {
                        // The clause does not start on the line after the previous clause.
                        this.AddViolation(element, clause.LineNumber, Rules.QueryClauseMustFollowPreviousClause);
                        return false;
                    }
                }

                previousClause = clause;

                if (clause.QueryClauseType == QueryClauseType.Continuation)
                {
                    QueryContinuationClause continuationClause = (QueryContinuationClause)clause;
                    if (!this.ProcessQueryClauses(
                        element,
                        expression,
                        continuationClause.ChildClauses,
                        ref previousClause,
                        ref clauseOnSameLine,
                        ref clauseOnSeparateLine))
                    {
                        return false;
                    }
                }
            }

            return true;
        }
        /// <summary>
        /// Reads a query expression.
        /// </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>
        /// <returns>Returns the expression.</returns>
        private QueryExpression GetQueryExpression(Reference<ICodePart> parentReference, bool unsafeCode)
        {
            Param.AssertNotNull(parentReference, "parentReference");
            Param.Ignore(unsafeCode);

            // Ensure that the expression starts with the keyword 'from'.
            Symbol symbol = this.GetNextSymbol(parentReference);

            Debug.Assert(
                symbol != null &&
                symbol.SymbolType == SymbolType.Other &&
                string.CompareOrdinal(symbol.Text, "from") == 0,
                "Expected a from keyword");

            var expressionReference = new Reference<ICodePart>();

            // Stores the list of clauses in the expression.
            List<QueryClause> clauses = new List<QueryClause>();

            // The variables defined by the clauses in this expression.
            List<Variable> variables = new List<Variable>();

            // Extract the clauses.
            CsTokenList clauseTokens = this.GetQueryExpressionClauses(expressionReference, unsafeCode, clauses, variables);
            if (clauses.Count == 0 || clauseTokens.First == null)
            {
                throw this.CreateSyntaxException();
            }

            // Create and return the expression.
            QueryExpression queryExpression = new QueryExpression(clauseTokens, clauses.ToArray());
            expressionReference.Target = queryExpression;
            queryExpression.Variables.AddRange(variables);

            return queryExpression;
        }
Example #4
0
 private QueryExpression GetQueryExpression(bool unsafeCode)
 {
     this.GetNextSymbol();
     List<QueryClause> clauses = new List<QueryClause>();
     List<Variable> variables = new List<Variable>();
     CsTokenList tokens = this.GetQueryExpressionClauses(unsafeCode, clauses, variables);
     if ((clauses.Count == 0) || (tokens.First == null))
     {
         throw this.CreateSyntaxException();
     }
     QueryExpression expression = new QueryExpression(tokens, clauses.ToArray());
     expression.Variables.AddRange(variables);
     return expression;
 }