Пример #1
0
        /// <summary>
        /// Initializes the expression from the list of clauses.
        /// </summary>
        /// <param name="items">
        /// The list of clauses in the expression.
        /// </param>
        private void InitializeFromClauses(IEnumerable <QueryClause> items)
        {
            Param.AssertNotNull(items, "items");

            foreach (QueryClause clause in items)
            {
                foreach (Expression expression in clause.ChildExpressions)
                {
                    this.AddExpression(expression);
                }

                QueryContinuationClause continuationClause = clause as QueryContinuationClause;
                if (continuationClause != null)
                {
                    this.InitializeFromClauses(continuationClause.ChildClauses);
                }
            }
        }
Пример #2
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);
        }
Пример #3
0
        /// <summary>
        /// Gets a query continuation clause.
        /// </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 query continuation clause.
        /// </returns>
        private QueryContinuationClause GetQueryContinuationClause(Reference<ICodePart> parentReference, bool unsafeCode)
        {
            Param.AssertNotNull(parentReference, "parentReference");
            Param.Ignore(unsafeCode);

            // Get and add the 'into' symbol.
            Symbol symbol = this.GetNextSymbol(SymbolType.Other, parentReference);
            Debug.Assert(symbol.Text == "into", "Expected an into keyword");

            Reference<ICodePart> clauseReference = new Reference<ICodePart>();

            Node<CsToken> firstTokenNode = this.tokens.InsertLast(this.GetToken(CsTokenType.Into, SymbolType.Other, clauseReference));

            // Get the identifier.
            Variable rangeVariable = this.GetVariable(clauseReference, unsafeCode, true, true);
            if (rangeVariable == null)
            {
                throw this.CreateSyntaxException();
            }

            // Get the continuation clauses.
            List<QueryClause> clauses = new List<QueryClause>();

            // The variables defined by the clauses under this continuation clause.
            List<Variable> variables = new List<Variable>();

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

            // Create and return the clause.
            QueryContinuationClause continuationClause = new QueryContinuationClause(
                new CsTokenList(this.tokens, firstTokenNode, continuationClauseTokens.Last), rangeVariable, clauses.ToArray());

            clauseReference.Target = continuationClause;
            continuationClause.Variables.AddRange(variables);

            return continuationClause;
        }
        /// <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);
        }