/// <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; }
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; }