/// <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"); Reference<ICodePart> 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; }
/// <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); }