提交 f900a819 编写于 作者: M Manish Vasani

Add IOperation support for query operations.

Implements the proposal discussed in https://github.com/dotnet/roslyn/issues/17838#issuecomment-318125250.

1. We will have an `IQueryExpression` representing the topmost query expression.
2. This will point to the last query clause (`IQueryClause`) or continuation (`IQueryContinuation`) in the unrolled lowered bound tree - we are not got going to reverse the tree to match source.
3. `IQueryClause` will have a `QueryClauseKind` field indicating the type of query clause, which should match the syntax/language specification query clause/operator kinds.
上级 68e37cf1
......@@ -218,6 +218,8 @@ private IOperation CreateInternal(BoundNode boundNode)
return CreateBoundPatternSwitchLabelOperation((BoundPatternSwitchLabel)boundNode);
case BoundKind.IsPatternExpression:
return CreateBoundIsPatternExpressionOperation((BoundIsPatternExpression)boundNode);
case BoundKind.QueryClause:
return CreateBoundQueryClauseOrExpressionOperation((BoundQueryClause)boundNode);
default:
var constantValue = ConvertToOptional((boundNode as BoundExpression)?.ConstantValue);
return Operation.CreateOperationNone(boundNode.Syntax, constantValue, getChildren: () => GetIOperationChildren(boundNode));
......@@ -1335,5 +1337,136 @@ private IIsPatternExpression CreateBoundIsPatternExpressionOperation(BoundIsPatt
Optional<object> constantValue = ConvertToOptional(boundIsPatternExpression.ConstantValue);
return new LazyIsPatternExpression(expression, pattern, syntax, type, constantValue);
}
private IOperation CreateBoundQueryClauseOrExpressionOperation(BoundQueryClause boundQueryClause)
{
var queryClauseKindOpt = GetQueryClauseKind(boundQueryClause);
return queryClauseKindOpt.HasValue ?
CreateBoundQueryClauseOperation(boundQueryClause, queryClauseKindOpt.Value) :
CreateBoundQueryOrContinuationOrOrderExpressionOperation(boundQueryClause);
}
private IQueryClause CreateBoundQueryClauseOperation(BoundQueryClause boundQueryClause, QueryClauseKind queryClauseKind)
{
Lazy<IOperation> underlyingExpression = new Lazy<IOperation>(() => Create(boundQueryClause.Value));
SyntaxNode syntax = boundQueryClause.Syntax;
ITypeSymbol type = boundQueryClause.Type;
Optional<object> constantValue = ConvertToOptional(boundQueryClause.ConstantValue);
switch(queryClauseKind)
{
case QueryClauseKind.FromClause:
return new LazyFromQueryClause(underlyingExpression, syntax, type, constantValue);
case QueryClauseKind.SelectClause:
return new LazySelectQueryClause(underlyingExpression, syntax, type, constantValue);
case QueryClauseKind.WhereClause:
return new LazyWhereQueryClause(underlyingExpression, syntax, type, constantValue);
case QueryClauseKind.LetClause:
return new LazyLetQueryClause(underlyingExpression, syntax, type, constantValue);
case QueryClauseKind.OrderByClause:
return new LazyOrderByQueryClause(underlyingExpression, syntax, type, constantValue);
case QueryClauseKind.GroupByClause:
return new LazyGroupByQueryClause(underlyingExpression, syntax, type, constantValue);
case QueryClauseKind.JoinClause:
return new LazyJoinQueryClause(underlyingExpression, syntax, type, constantValue);
case QueryClauseKind.JoinIntoClause:
return new LazyJoinIntoQueryClause(underlyingExpression, syntax, type, constantValue);
default:
throw ExceptionUtilities.Unreachable;
}
}
private IOperation CreateBoundQueryOrContinuationOrOrderExpressionOperation(BoundQueryClause boundQueryClause)
{
switch(boundQueryClause.Syntax.Kind())
{
case SyntaxKind.QueryExpression:
return CreateBoundQueryExpressionOperation(boundQueryClause);
case SyntaxKind.QueryContinuation:
return CreateBoundQueryContinuationOperation(boundQueryClause);
case SyntaxKind.AscendingOrdering:
return CreateBoundOrderingExpressionOperation(boundQueryClause, OrderKind.Ascending);
case SyntaxKind.DescendingOrdering:
return CreateBoundOrderingExpressionOperation(boundQueryClause, OrderKind.Descending);
case SyntaxKind.QueryBody:
return boundQueryClause.Value != null ? CreateBoundQueryClauseOrExpressionOperation((BoundQueryClause)boundQueryClause.Value) : null;
default:
throw ExceptionUtilities.Unreachable;
}
}
private IQueryExpression CreateBoundQueryExpressionOperation(BoundQueryClause boundQueryClause)
{
Lazy<IOperation> lastClauseOrContinuation = new Lazy<IOperation>(() => Create(boundQueryClause.Value));
SyntaxNode syntax = boundQueryClause.Syntax;
ITypeSymbol type = boundQueryClause.Type;
Optional<object> constantValue = ConvertToOptional(boundQueryClause.ConstantValue);
return new LazyQueryExpression(lastClauseOrContinuation, syntax, type, constantValue);
}
private IQueryContinuation CreateBoundQueryContinuationOperation(BoundQueryClause boundQueryClause)
{
Lazy<IOperation> queryBody = new Lazy<IOperation>(() => Create(boundQueryClause.Value));
IRangeVariableSymbol definedSymbol = boundQueryClause.DefinedSymbol;
SyntaxNode syntax = boundQueryClause.Syntax;
ITypeSymbol type = boundQueryClause.Type;
Optional<object> constantValue = ConvertToOptional(boundQueryClause.ConstantValue);
return new LazyQueryContinuation(queryBody, definedSymbol, syntax, type, constantValue);
}
private IOrderingExpression CreateBoundOrderingExpressionOperation(BoundQueryClause boundQueryClause, OrderKind orderKind)
{
Lazy<IOperation> expression = new Lazy<IOperation>(() => Create(boundQueryClause.Value));
SyntaxNode syntax = boundQueryClause.Syntax;
ITypeSymbol type = boundQueryClause.Type;
Optional<object> constantValue = ConvertToOptional(boundQueryClause.ConstantValue);
return new LazyOrderingExpression(expression, orderKind, syntax, type, constantValue);
}
private QueryClauseKind? GetQueryClauseKind(BoundQueryClause boundQueryClause)
{
switch(boundQueryClause.Syntax.Kind())
{
case SyntaxKind.FromClause:
return QueryClauseKind.FromClause;
case SyntaxKind.SelectClause:
return QueryClauseKind.SelectClause;
case SyntaxKind.WhereClause:
return QueryClauseKind.WhereClause;
case SyntaxKind.LetClause:
return QueryClauseKind.LetClause;
case SyntaxKind.OrderByClause:
return QueryClauseKind.OrderByClause;
case SyntaxKind.GroupClause:
return QueryClauseKind.GroupByClause;
case SyntaxKind.JoinClause:
return QueryClauseKind.JoinClause;
case SyntaxKind.JoinIntoClause:
return QueryClauseKind.JoinIntoClause;
default:
return null;
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
......@@ -138,29 +138,19 @@ public void M(List<Customer> customers)
}
";
string expectedOperationTree = @"
IOperation: (OperationKind.None) (Syntax: 'from cust i ... t cust.Name')
Children(1):
IOperation: (OperationKind.None) (Syntax: 'select cust.Name')
Children(1):
IInvocationExpression (System.Collections.Generic.IEnumerable<System.String> System.Linq.Enumerable.Select<Customer, System.String>(this System.Collections.Generic.IEnumerable<Customer> source, System.Func<Customer, System.String> selector)) (OperationKind.InvocationExpression, Type: System.Collections.Generic.IEnumerable<System.String>) (Syntax: 'select cust.Name')
Instance Receiver: null
Arguments(2):
IArgument (ArgumentKind.Explicit, Matching Parameter: source) (OperationKind.Argument) (Syntax: 'from cust in customers')
IConversionExpression (ConversionKind.Cast, Implicit) (OperationKind.ConversionExpression, Type: System.Collections.Generic.IEnumerable<Customer>) (Syntax: 'from cust in customers')
Operand: IOperation: (OperationKind.None) (Syntax: 'from cust in customers')
Children(1):
IParameterReferenceExpression: customers (OperationKind.ParameterReferenceExpression, Type: System.Collections.Generic.List<Customer>) (Syntax: 'customers')
InConversion: null
OutConversion: null
IArgument (ArgumentKind.Explicit, Matching Parameter: selector) (OperationKind.Argument) (Syntax: 'cust.Name')
IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: System.Func<Customer, System.String>) (Syntax: 'cust.Name')
Operand: ILambdaExpression (Signature: lambda expression) (OperationKind.LambdaExpression, Type: null) (Syntax: 'cust.Name')
IBlockStatement (1 statements) (OperationKind.BlockStatement) (Syntax: 'cust.Name')
IReturnStatement (OperationKind.ReturnStatement) (Syntax: 'cust.Name')
ReturnedValue: IPropertyReferenceExpression: System.String Customer.Name { get; set; } (OperationKind.PropertyReferenceExpression, Type: System.String) (Syntax: 'cust.Name')
Instance Receiver: IOperation: (OperationKind.None) (Syntax: 'cust')
InConversion: null
OutConversion: null
IQueryExpression (OperationKind.QueryExpression, Type: System.Collections.Generic.IEnumerable(Of System.String)) (Syntax: 'From cust I ... t cust.Name')
LastClauseOrContinuation: ISelectQueryClause (Clause kind: SelectClause) (OperationKind.QueryClause) (Syntax: 'Select cust.Name')
ReducedExpression: IInvocationExpression ( Function System.Collections.Generic.IEnumerable(Of Customer).Select(Of System.String)(selector As System.Func(Of Customer, System.String)) As System.Collections.Generic.IEnumerable(Of System.String)) (OperationKind.InvocationExpression, Type: System.Collections.Generic.IEnumerable(Of System.String)) (Syntax: 'Select cust.Name')
Instance Receiver: IConversionExpression (ConversionKind.Cast, Implicit) (OperationKind.ConversionExpression, Type: System.Collections.Generic.IEnumerable(Of Customer)) (Syntax: 'cust In customers')
Operand: IFromQueryClause (Clause kind: FromClause) (OperationKind.QueryClause) (Syntax: 'From cust In customers')
ReducedExpression: IParameterReferenceExpression: customers (OperationKind.ParameterReferenceExpression, Type: System.Collections.Generic.List(Of Customer)) (Syntax: 'customers')
Arguments(1):
IArgument (ArgumentKind.DefaultValue, Matching Parameter: selector) (OperationKind.Argument) (Syntax: 'cust.Name')
IConversionExpression (ConversionKind.Basic, Implicit) (OperationKind.ConversionExpression, Type: System.Func(Of Customer, System.String)) (Syntax: 'cust.Name')
Operand: IPropertyReferenceExpression: Property Customer.Name As System.String (OperationKind.PropertyReferenceExpression, Type: System.String) (Syntax: 'cust.Name')
Instance Receiver: IOperation: (OperationKind.None) (Syntax: 'cust')
InConversion: null
OutConversion: null
";
var expectedDiagnostics = DiagnosticDescription.None;
......
......@@ -142,6 +142,10 @@ public enum OperationKind
DynamicObjectCreationExpression = 0x125,
/// <summary>Indicates an <see cref="IDynamicMemberReferenceExpression"/>.</summary>
DynamicMemberReferenceExpression = 0x126,
/// <summary>Indicates an <see cref="IQueryExpression"/>.</summary>
QueryExpression = 0x127,
/// <summary>Indicates an <see cref="IOrderingExpression"/> in an <see cref="IOrderByQueryClause"/>.</summary>
OrderingExpression = 0x128,
// Expressions that occur only in C#.
......@@ -166,7 +170,8 @@ public enum OperationKind
/// <summary>Indicates an <see cref="IOmittedArgumentExpression"/>.</summary>
OmittedArgumentExpression = 0x300,
// 0x301 was removed, and is available for use.
/// <summary>Indicates an <see cref="IAggregationExpression"/>.</summary>
AggregationExpression = 0x301,
/// <summary>Indicates an <see cref="IPlaceholderExpression"/>.</summary>
PlaceholderExpression = 0x302,
......@@ -216,5 +221,10 @@ public enum OperationKind
/// <summary>Indicates an <see cref="IDefaultCaseClause"/>.</summary>
DefaultCaseClause = 0x412,
/// <summary>Indicates an <see cref="IQueryClause"/>.</summary>
QueryClause = 0x413,
/// <summary>Indicates an <see cref="IQueryContinuation"/>.</summary>
QueryContinuation = 0x414,
}
}
......@@ -464,6 +464,101 @@ public virtual void VisitTupleExpression(ITupleExpression operation)
{
DefaultVisit(operation);
}
public virtual void VisitQueryExpression(IQueryExpression operation)
{
DefaultVisit(operation);
}
public virtual void VisitOrderingExpression(IOrderingExpression operation)
{
DefaultVisit(operation);
}
public virtual void VisitAggregationExpression(IAggregationExpression operation)
{
DefaultVisit(operation);
}
public virtual void VisitQueryContinuation(IQueryContinuation operation)
{
DefaultVisit(operation);
}
public virtual void VisitFromQueryClause(IFromQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitSelectQueryClause(ISelectQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitWhereQueryClause(IWhereQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitLetQueryClause(ILetQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitOrderByQueryClause(IOrderByQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitGroupByQueryClause(IGroupByQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitGroupJoinQueryClause(IGroupJoinQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitJoinQueryClause(IJoinQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitJoinIntoQueryClause(IJoinIntoQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitDistinctQueryClause(IDistinctQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitAggregateQueryClause(IAggregateQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitSkipQueryClause(ISkipQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitSkipWhileQueryClause(ISkipWhileQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitTakeQueryClause(ITakeQueryClause operation)
{
DefaultVisit(operation);
}
public virtual void VisitTakeWhileQueryClause(ITakeWhileQueryClause operation)
{
DefaultVisit(operation);
}
}
/// <summary>
......@@ -934,5 +1029,100 @@ public virtual TResult VisitTupleExpression(ITupleExpression operation, TArgumen
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitQueryExpression(IQueryExpression operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitOrderingExpression(IOrderingExpression operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitAggregationExpression(IAggregationExpression operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitQueryContinuation(IQueryContinuation operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitFromQueryClause(IFromQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitSelectQueryClause(ISelectQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitWhereQueryClause(IWhereQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitLetQueryClause(ILetQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitOrderByQueryClause(IOrderByQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitGroupByQueryClause(IGroupByQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitGroupJoinQueryClause(IGroupJoinQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitJoinQueryClause(IJoinQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitJoinIntoQueryClause(IJoinIntoQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitDistinctQueryClause(IDistinctQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitAggregateQueryClause(IAggregateQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitSkipQueryClause(ISkipQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitSkipWhileQueryClause(ISkipWhileQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitTakeQueryClause(ITakeQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitTakeWhileQueryClause(ITakeWhileQueryClause operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents an aggregate query clause in VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IAggregateQueryClause : IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a group or function aggregation expression inside an Into clause of a Group By or Aggregate query clause in VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IAggregationExpression : IOperation
{
/// <summary>
/// Flag indicating if this is a group aggregation clause.
/// </summary>
bool IsGroupAggregation { get; }
/// <summary>
/// Aggregation expression.
/// </summary>
IOperation Expression { get; }
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a distinct query clause in VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IDistinctQueryClause : IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a from query clause in C# or VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IFromQueryClause: IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a group by query clause in C# or VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IGroupByQueryClause: IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a group join query clause in VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IGroupJoinQueryClause : IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a join into query clause in C#.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IJoinIntoQueryClause : IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a join query clause in C# or VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IJoinQueryClause : IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a let query clause in C# or VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface ILetQueryClause : IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents an order by query clause in C# or VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IOrderByQueryClause: IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents an ordering expression within an <see cref="IOrderByQueryClause"/> in C# or VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IOrderingExpression : IOperation
{
/// <summary>
/// <see cref="OrderKind"/> for the ordering expression.
/// </summary>
OrderKind OrderKind { get; }
/// <summary>
/// Underlying ordering expression for the order by query clause.
/// </summary>
IOperation Expression { get; }
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a query clause in C# or VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IQueryClause : IOperation
{
/// <summary>
/// <see cref="QueryClauseKind"/> of the clause.
/// </summary>
QueryClauseKind ClauseKind { get; }
/// <summary>
/// Underlying reduced expression for the query clause. This is normally the invocation expression for the underlying linq call.
/// </summary>
IOperation ReducedExpression { get; }
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a query continuation in C#.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IQueryContinuation : IOperation
{
/// <summary>
/// Declared symbol.
/// </summary>
IRangeVariableSymbol DeclaredSymbol { get; }
/// <summary>
/// Query body of the continuation.
/// </summary>
IOperation QueryBody { get; }
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a query expression in C# or VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IQueryExpression : IOperation
{
/// <summary>
/// Last <see cref="IQueryClause"/> or <see cref="IQueryContinuation"/> in the unrolled query expression.
/// For example, for the query expression "from x in set where x.Name != null select x.Name", the select clause is the last clause of the unrolled query expression,
/// with the where clause as one of its descendant, and the from clause as the descendant of the where clause.
/// </summary>
IOperation LastClauseOrContinuation { get; }
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a select query clause in C# or VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface ISelectQueryClause: IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a skip query clause in VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface ISkipQueryClause : IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a skip while query clause in VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface ISkipWhileQueryClause : IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a take query clause in VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface ITakeQueryClause : IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a take while query clause in VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface ITakeWhileQueryClause : IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents a where query clause in C# or VB.
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IWhereQueryClause: IQueryClause
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Kind of ordering for an <see cref="IOrderingExpression"/>
/// </summary>
public enum OrderKind
{
/// <summary>
/// Represents no ordering for an <see cref="IOrderingExpression"/>.
/// </summary>
None = 0x0,
/// <summary>
/// Represents an ascending ordering for an <see cref="IOrderingExpression"/>.
/// </summary>
Ascending = 0x1,
/// <summary>
/// Represents an ascending ordering for an <see cref="IOrderingExpression"/>.
/// </summary>
Descending = 0x2,
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Kind of <see cref="IQueryClause"/> within an <see cref="IQueryExpression"/>.
/// </summary>
public enum QueryClauseKind
{
/// <summary> Indicates an invalid clause kind.</summary>
None = 0x00,
/// <summary>Indicates an <see cref="IFromQueryClause"/> in C# and VB.</summary>
FromClause = 0x01,
/// <summary>Indicates an <see cref="ISelectQueryClause"/> in C# and VB.</summary>
SelectClause = 0x02,
/// <summary>Indicates an <see cref="IWhereQueryClause"/> in C# and VB.</summary>
WhereClause = 0x03,
/// <summary>Indicates an <see cref="ILetQueryClause"/> in C# and VB.</summary>
LetClause = 0x04,
/// <summary>Indicates an <see cref="IOrderByQueryClause"/> in C# and VB.</summary>
OrderByClause = 0x05,
/// <summary>Indicates an <see cref="GroupByQueryClause"/> in C# and VB.</summary>
GroupByClause = 0x06,
/// <summary>Indicates an <see cref="IGroupJoinQueryClause"/> in VB.</summary>
GroupJoinClause = 0x07,
/// <summary>Indicates an <see cref="IJoinQueryClause"/> in C# and VB.</summary>
JoinClause = 0x08,
/// <summary>Indicates an <see cref="IJoinIntoQueryClause"/> in C#.</summary>
JoinIntoClause = 0x09,
/// <summary>Indicates an <see cref="IDistinctQueryClause"/> in VB.</summary>
DistinctClause = 0x0a,
/// <summary>Indicates an <see cref="IAggregateQueryClause"/> in VB.</summary>
AggregateClause = 0x0b,
/// <summary>Indicates an <see cref="ISkipQueryClause"/> in VB.</summary>
SkipClause = 0x0c,
/// <summary>Indicates an <see cref="ISkipWhileQueryClause"/> in VB.</summary>
SkipWhileClause = 0x0d,
/// <summary>Indicates an <see cref="ITakeQueryClause"/> in VB.</summary>
TakeClause = 0x0e,
/// <summary>Indicates an <see cref="ITakeWhileQueryClause"/> in VB.</summary>
TakeWhileClause = 0x0f,
}
}
......@@ -4,6 +4,7 @@ Imports System.Collections.Concurrent
Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.Semantics
Partial Friend NotInheritable Class VisualBasicOperationFactory
......@@ -182,6 +183,30 @@ Namespace Microsoft.CodeAnalysis.Semantics
Return Create(DirectCast(boundNode, BoundAnonymousTypeFieldInitializer).Value)
Case BoundKind.AnonymousTypePropertyAccess
Return CreateBoundAnonymousTypePropertyAccessOperation(DirectCast(boundNode, BoundAnonymousTypePropertyAccess))
Case BoundKind.QueryExpression
Return CreateBoundQueryExpressionOperation(DirectCast(boundNode, BoundQueryExpression))
Case BoundKind.QueryClause
Return CreateBoundQueryClauseOperation(DirectCast(boundNode, BoundQueryClause))
Case BoundKind.QueryableSource
Return CreateBoundQueryableSourceOperation(DirectCast(boundNode, BoundQueryableSource))
Case BoundKind.AggregateClause
Return CreateBoundAggregateClauseOperation(DirectCast(boundNode, BoundAggregateClause))
Case BoundKind.Ordering
Return CreateBoundOrderingOperation(DirectCast(boundNode, BoundOrdering))
Case BoundKind.GroupAggregation
Return CreateBoundGroupAggregationOperation((DirectCast(boundNode, BoundGroupAggregation)))
Case BoundKind.QuerySource
' Query source has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundQuerySource).Expression)
Case BoundKind.ToQueryableCollectionConversion
' Queryable collection conversion has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundToQueryableCollectionConversion).ConversionCall)
Case BoundKind.QueryLambda
' Query lambda has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundQueryLambda).Expression)
Case BoundKind.RangeVariableAssignment
' Range variable assignment has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundRangeVariableAssignment).Value)
Case Else
Dim constantValue = ConvertToOptional(TryCast(boundNode, BoundExpression)?.ConstantValueOpt)
Return Operation.CreateOperationNone(boundNode.Syntax, constantValue, Function() GetIOperationChildren(boundNode))
......@@ -1094,6 +1119,140 @@ Namespace Microsoft.CodeAnalysis.Semantics
Dim constantValue As [Optional](Of Object) = ConvertToOptional(boundAnonymousTypePropertyAccess.ConstantValueOpt)
Return New LazyPropertyReferenceExpression([property], instance, member, argumentsInEvaluationOrder, syntax, type, constantValue)
End Function
Private Function CreateBoundQueryExpressionOperation(boundQueryExpression As BoundQueryExpression) As IQueryExpression
Dim lastClauseOrContinuation As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() Create(boundQueryExpression.LastOperator))
Dim syntax As SyntaxNode = boundQueryExpression.Syntax
Dim type As ITypeSymbol = boundQueryExpression.Type
Dim constantValue As [Optional](Of Object) = ConvertToOptional(boundQueryExpression.ConstantValueOpt)
Return New LazyQueryExpression(lastClauseOrContinuation, syntax, type, constantValue)
End Function
Private Function CreateBoundQueryClauseOperation(boundQueryClause As BoundQueryClause) As IOperation
Return CreateBoundQueryClauseOperation(boundQueryClause, boundQueryClause.UnderlyingExpression)
End Function
Private Function CreateBoundQueryableSourceOperation(boundQueryClause As BoundQueryableSource) As IOperation
Return CreateBoundQueryClauseOperation(boundQueryClause, boundQueryClause.Source)
End Function
Private Function CreateBoundQueryClauseOperation(boundQueryClause As BoundQueryClauseBase, underlyingExpression As BoundExpression) As IOperation
Dim syntax As SyntaxNode = boundQueryClause.Syntax
Dim clauseSyntaxKind = syntax.Kind
If boundQueryClause.WasCompilerGenerated AndAlso clauseSyntaxKind = SyntaxKind.FromClause Then
' Handle implicit select clause
' VB generates bound query clause with From syntax only for implicit select.
' The actual from clauses are generated for the underlying range variable declaration syntax nodes
clauseSyntaxKind = SyntaxKind.SelectClause
ElseIf clauseSyntaxKind = SyntaxKind.ExpressionRangeVariable OrElse clauseSyntaxKind = SyntaxKind.CollectionRangeVariable Then
' VB Binder does not generate a bound node for few query clause syntax nodes (From and Let in the current implementation).
' So we generate an Operation node for such nodes when we encounter the bound node for the first range variable child.
If IsFirstRangeVariableChildWhoseParentHasNoBoundNode(boundQueryClause) Then
syntax = syntax.Parent
clauseSyntaxKind = syntax.Kind
Else
' Currently we do not generate IOperation nodes for range variable declarations.
Return Create(underlyingExpression)
End If
End If
Dim reducedExpression As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() Create(underlyingExpression))
Dim type As ITypeSymbol = boundQueryClause.Type
Dim constantValue As [Optional](Of Object) = ConvertToOptional(boundQueryClause.ConstantValueOpt)
Select Case clauseSyntaxKind
Case SyntaxKind.FromClause
Return New LazyFromQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.SelectClause
Return New LazySelectQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.WhereClause
Return New LazyWhereQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.LetClause
Return New LazyLetQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.OrderByClause
Return New LazyOrderByQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.GroupByClause
Return New LazyGroupByQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.GroupJoinClause
Return New LazyGroupJoinQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.SimpleJoinClause
Return New LazyJoinQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.DistinctClause
Return New LazyDistinctQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.AggregateClause
Return New LazyAggregateQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.SkipClause
Return New LazySkipQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.SkipWhileClause
Return New LazySkipWhileQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.TakeClause
Return New LazyTakeQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.TakeWhileClause
Return New LazyTakeWhileQueryClause(reducedExpression, syntax, type, constantValue)
Case SyntaxKind.FunctionAggregation
Return New LazyAggregationExpression(reducedExpression, isGroupAggregation:=False, syntax:=syntax, type:=type, constantValue:=constantValue)
Case Else
Throw ExceptionUtilities.Unreachable
End Select
End Function
Private Function IsFirstRangeVariableChildWhoseParentHasNoBoundNode(boundQueryClause As BoundQueryClauseBase) As Boolean
' VB Binder does not generate a bound node for few query clause syntax nodes (From and Let in the current implementation).
' So we generate an Operation node for such nodes when we encounter the bound node for the first range variable child.
Select Case boundQueryClause.Syntax.Parent.Kind
Case SyntaxKind.FromClause, SyntaxKind.LetClause
Return IsFirstRangeVariableChild(boundQueryClause)
Case Else
Return False
End Select
End Function
Private Function IsFirstRangeVariableChild(boundQueryClause As BoundQueryClauseBase) As Boolean
Select Case boundQueryClause.Syntax.Kind
Case SyntaxKind.ExpressionRangeVariable, SyntaxKind.CollectionRangeVariable
Return boundQueryClause.Syntax.Parent.ChildNodes().First Is boundQueryClause.Syntax
Case Else
Return False
End Select
End Function
Private Function CreateBoundAggregateClauseOperation(boundAggregateClause As BoundAggregateClause) As IAggregateQueryClause
Dim reducedExpression As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() Create(boundAggregateClause.UnderlyingExpression))
Dim syntax As SyntaxNode = boundAggregateClause.Syntax
Dim type As ITypeSymbol = boundAggregateClause.Type
Dim constantValue As [Optional](Of Object) = ConvertToOptional(boundAggregateClause.ConstantValueOpt)
Return New LazyAggregateQueryClause(reducedExpression, syntax, type, constantValue)
End Function
Private Function CreateBoundOrderingOperation(boundOrdering As BoundOrdering) As IOrderingExpression
Dim expression As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() Create(boundOrdering.UnderlyingExpression))
Dim orderKind = GetOrderKind(DirectCast(boundOrdering.Syntax, OrderingSyntax))
Dim syntax As SyntaxNode = boundOrdering.Syntax
Dim type As ITypeSymbol = boundOrdering.Type
Dim constantValue As [Optional](Of Object) = ConvertToOptional(boundOrdering.ConstantValueOpt)
Return New LazyOrderingExpression(expression, orderKind, syntax, type, constantValue)
End Function
Private Shared Function GetOrderKind(orderingSyntax As OrderingSyntax) As OrderKind
Select Case orderingSyntax.Kind
Case SyntaxKind.AscendingOrdering
Return OrderKind.Ascending
Case SyntaxKind.DescendingOrdering
Return OrderKind.Descending
Case Else
Return OrderKind.None
End Select
End Function
Private Function CreateBoundGroupAggregationOperation(boundGroupAggregation As BoundGroupAggregation) As IAggregationExpression
Dim expression As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() Create(boundGroupAggregation.Group))
Dim isGroupAggregation = True
Dim syntax As SyntaxNode = boundGroupAggregation.Syntax
Dim type As ITypeSymbol = boundGroupAggregation.Type
Dim constantValue As [Optional](Of Object) = ConvertToOptional(boundGroupAggregation.ConstantValueOpt)
Return New LazyAggregationExpression(expression, isGroupAggregation, syntax, type, constantValue)
End Function
End Class
End Namespace
......
......@@ -1264,6 +1264,141 @@ public override void VisitPatternCaseClause(IPatternCaseClause operation)
Visit(operation.GuardExpression, "Guard Expression");
}
public override void VisitQueryExpression(IQueryExpression operation)
{
LogString(nameof(IQueryExpression));
LogCommonPropertiesAndNewLine(operation);
Visit(operation.LastClauseOrContinuation, "LastClauseOrContinuation");
}
public override void VisitOrderingExpression(IOrderingExpression operation)
{
LogString(nameof(IOrderingExpression));
LogString($" (Order kind: {operation.OrderKind})");
LogCommonPropertiesAndNewLine(operation);
Visit(operation.Expression, "Expression");
}
public override void VisitAggregationExpression(IAggregationExpression operation)
{
LogString(nameof(IAggregationExpression));
var aggregationkind = operation.IsGroupAggregation ? "Group" : "Function";
LogString($" (Aggregation Kind: {aggregationkind})");
LogCommonPropertiesAndNewLine(operation);
Visit(operation.Expression, "Expression");
}
public override void VisitQueryContinuation(IQueryContinuation operation)
{
LogString(nameof(IQueryContinuation));
LogSymbol(operation.DeclaredSymbol, " (Declared symbol");
LogString(")");
LogCommonPropertiesAndNewLine(operation);
Visit(operation.QueryBody, "Query Body");
}
private void VisitQueryClauseCommon(IQueryClause operation)
{
LogString($" (Clause kind: {operation.ClauseKind})");
LogCommonPropertiesAndNewLine(operation);
Visit(operation.ReducedExpression, "ReducedExpression");
}
public override void VisitFromQueryClause(IFromQueryClause operation)
{
LogString(nameof(IFromQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitSelectQueryClause(ISelectQueryClause operation)
{
LogString(nameof(ISelectQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitWhereQueryClause(IWhereQueryClause operation)
{
LogString(nameof(IWhereQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitLetQueryClause(ILetQueryClause operation)
{
LogString(nameof(ILetQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitOrderByQueryClause(IOrderByQueryClause operation)
{
LogString(nameof(IOrderByQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitGroupByQueryClause(IGroupByQueryClause operation)
{
LogString(nameof(IGroupByQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitGroupJoinQueryClause(IGroupJoinQueryClause operation)
{
LogString(nameof(IGroupJoinQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitJoinQueryClause(IJoinQueryClause operation)
{
LogString(nameof(IJoinQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitJoinIntoQueryClause(IJoinIntoQueryClause operation)
{
LogString(nameof(IJoinIntoQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitAggregateQueryClause(IAggregateQueryClause operation)
{
LogString(nameof(IAggregateQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitDistinctQueryClause(IDistinctQueryClause operation)
{
LogString(nameof(IDistinctQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitSkipQueryClause(ISkipQueryClause operation)
{
LogString(nameof(ISkipQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitSkipWhileQueryClause(ISkipWhileQueryClause operation)
{
LogString(nameof(ISkipWhileQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitTakeQueryClause(ITakeQueryClause operation)
{
LogString(nameof(ITakeQueryClause));
VisitQueryClauseCommon(operation);
}
public override void VisitTakeWhileQueryClause(ITakeWhileQueryClause operation)
{
LogString(nameof(ITakeWhileQueryClause));
VisitQueryClauseCommon(operation);
}
#endregion
}
}
......@@ -598,5 +598,106 @@ public override void VisitPatternCaseClause(IPatternCaseClause operation)
base.VisitPatternCaseClause(operation);
}
public override void VisitQueryExpression(IQueryExpression operation)
{
base.VisitQueryExpression(operation);
}
public override void VisitOrderingExpression(IOrderingExpression operation)
{
var orderKind = operation.OrderKind;
base.VisitOrderingExpression(operation);
}
public override void VisitAggregationExpression(IAggregationExpression operation)
{
var isGroupAggregation = operation.IsGroupAggregation;
base.VisitAggregationExpression(operation);
}
public override void VisitQueryContinuation(IQueryContinuation operation)
{
var declaredSymbol = operation.DeclaredSymbol;
base.VisitQueryContinuation(operation);
}
public override void VisitFromQueryClause(IFromQueryClause operation)
{
base.VisitFromQueryClause(operation);
}
public override void VisitSelectQueryClause(ISelectQueryClause operation)
{
base.VisitSelectQueryClause(operation);
}
public override void VisitWhereQueryClause(IWhereQueryClause operation)
{
base.VisitWhereQueryClause(operation);
}
public override void VisitLetQueryClause(ILetQueryClause operation)
{
base.VisitLetQueryClause(operation);
}
public override void VisitOrderByQueryClause(IOrderByQueryClause operation)
{
base.VisitOrderByQueryClause(operation);
}
public override void VisitGroupByQueryClause(IGroupByQueryClause operation)
{
base.VisitGroupByQueryClause(operation);
}
public override void VisitGroupJoinQueryClause(IGroupJoinQueryClause operation)
{
base.VisitGroupJoinQueryClause(operation);
}
public override void VisitJoinQueryClause(IJoinQueryClause operation)
{
base.VisitJoinQueryClause(operation);
}
public override void VisitJoinIntoQueryClause(IJoinIntoQueryClause operation)
{
base.VisitJoinIntoQueryClause(operation);
}
public override void VisitAggregateQueryClause(IAggregateQueryClause operation)
{
base.VisitAggregateQueryClause(operation);
}
public override void VisitDistinctQueryClause(IDistinctQueryClause operation)
{
base.VisitDistinctQueryClause(operation);
}
public override void VisitSkipQueryClause(ISkipQueryClause operation)
{
base.VisitSkipQueryClause(operation);
}
public override void VisitSkipWhileQueryClause(ISkipWhileQueryClause operation)
{
base.VisitSkipWhileQueryClause(operation);
}
public override void VisitTakeQueryClause(ITakeQueryClause operation)
{
base.VisitTakeQueryClause(operation);
}
public override void VisitTakeWhileQueryClause(ITakeWhileQueryClause operation)
{
base.VisitTakeWhileQueryClause(operation);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册