internal UsingCSharpStatement(ReadOnlyCollection <ParameterExpression> variables, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose)
 {
     Variables      = variables;
     Body           = body;
     AwaitInfo      = awaitInfo;
     PatternDispose = patternDispose;
 }
        /// <summary>
        /// Creates a <see cref="UsingCSharpStatement"/> that represents an await using statement.
        /// </summary>
        /// <param name="awaitInfo">The information required to await the DisposeAsync operation.</param>
        /// <param name="variables">The variables introduced by the statement.</param>
        /// <param name="declarations">The resources managed by the statement.</param>
        /// <param name="body">The body of the statement.</param>
        /// <param name="patternDispose">The (optional) lambda expression representing how to call the dispose method.</param>
        /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns>
        public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, IEnumerable <LocalDeclaration> declarations, Expression body, LambdaExpression patternDispose)
        {
            RequiresNotNull(declarations, nameof(declarations));

            AssertUsingAwaitInfo(ref awaitInfo, patternDispose);

            return(Using(awaitInfo, variables, declarations, body, patternDispose));
        }
        /// <summary>
        /// Creates a <see cref="UsingCSharpStatement"/> that represents an await using statement.
        /// </summary>
        /// <param name="awaitInfo">The information required to await the DisposeAsync operation.</param>
        /// <param name="variables">The variables introduced by the statement.</param>
        /// <param name="resource">The resource managed by the statement.</param>
        /// <param name="body">The body of the statement.</param>
        /// <param name="patternDispose">The (optional) lambda expression representing how to call the dispose method.</param>
        /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns>
        public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, Expression resource, Expression body, LambdaExpression patternDispose)
        {
            RequiresNotNull(resource, nameof(resource));

            AssertUsingAwaitInfo(ref awaitInfo, patternDispose);

            return(Using(awaitInfo, variables, resource, body, patternDispose));
        }
        public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, ParameterExpression variable, Expression resource, Expression body)
        {
            RequiresNotNull(variable, nameof(variable));

            var variables   = new[] { variable };
            var declaration = LocalDeclaration(variable, resource);

            return(AwaitUsing(awaitInfo, variables, new[] { declaration }, body, patternDispose: null));
        }
예제 #5
0
        /// <summary>
        /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will return this expression.
        /// </summary>
        /// <param name="operand">The <see cref="UnaryCSharpExpression.Operand" /> property of the result.</param>
        /// <param name="info">The <see cref="AwaitCSharpExpression.Info"/> property of the result.</param>
        /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
        public AwaitCSharpExpression Update(Expression operand, AwaitInfo info)
        {
            if (operand == Operand && info == Info)
            {
                return(this);
            }

            return(Rewrite(operand, info));
        }
예제 #6
0
        protected internal virtual AwaitInfo VisitAwaitInfo(AwaitInfo node)
        {
            if (node == null)
            {
                return(null);
            }

            return(node.Accept(this));
        }
예제 #7
0
        /// <summary>
        /// Creates an <see cref="AwaitCSharpExpression"/> that represents awaiting an asynchronous operation.
        /// </summary>
        /// <param name="operand">An <see cref="Expression" /> that specifies the asynchronous operation to await.</param>
        /// <param name="info">An <see cref="AwaitInfo"/> that specifies binding information for the await operation.</param>
        /// <returns>An instance of the <see cref="AwaitCSharpExpression"/>.</returns>
        public static AwaitCSharpExpression Await(Expression operand, AwaitInfo info)
        {
            // NB: This is the overload the C# compiler binds to.

            RequiresNotNull(operand, nameof(operand));
            RequiresNotNull(info, nameof(info));

            RequiresCanRead(operand, nameof(operand));

            info.RequiresCanBind(operand);

            return(new AwaitCSharpExpression(operand, info));
        }
 /// <summary>
 /// Creates a new <see cref="EnumeratorInfo"/> object providing binding information for foreach operations.
 /// </summary>
 /// <param name="isAsync">A Boolean indicating whether the enumeration is asynchronous.</param>
 /// <param name="collectionType">The type of the collection being enumerated.</param>
 /// <param name="getEnumerator">The <see cref="LambdaExpression"/> representing the operation used to obtain an enumerator from the collection.</param>
 /// <param name="moveNext">The <see cref="LambdaExpression"/> representing the operation used to invoke MoveNext or MoveNextAsync on the enumerator instance.</param>
 /// <param name="currentPropertyGetMethod">The get method of the Current property on the enumerator.</param>
 /// <param name="currentConversion">The <see cref="LambdaExpression"/> representing the conversion of the object returned from the Current property to the element type.</param>
 /// <param name="elementType">The type of the elements obtained by the iteration.</param>
 /// <param name="needsDisposal">A Boolean indicating whether a call to Dispose or DisposeAsync is required.</param>
 /// <param name="disposeAwaitInfo">The information required to await the DisposeAsync operation for await foreach statements.</param>
 /// <param name="patternDispose">The (optional) <see cref="LambdaExpression"/> representing how to call the dispose method.</param>
 /// <returns>A <see cref="EnumeratorInfo"/> object providing binding information for foreach operations.</returns>
 public static EnumeratorInfo EnumeratorInfo(
     bool isAsync,
     Type collectionType,
     LambdaExpression getEnumerator,
     LambdaExpression moveNext,
     MethodInfo currentPropertyGetMethod,
     LambdaExpression currentConversion,
     Type elementType,
     bool needsDisposal,
     AwaitInfo disposeAwaitInfo,
     LambdaExpression patternDispose) =>
 EnumeratorInfo(
     isAsync,
     collectionType,
     getEnumerator,
     moveNext,
     GetProperty(currentPropertyGetMethod),
     currentConversion,
     elementType,
     needsDisposal,
     disposeAwaitInfo,
     patternDispose
     );
 internal EnumeratorInfo(
     bool isAsync,
     Type collectionType,
     LambdaExpression getEnumerator,
     LambdaExpression moveNext,
     PropertyInfo current,
     LambdaExpression currentConversion,
     Type elementType,
     bool needsDisposal,
     AwaitInfo disposeAwaitInfo,
     LambdaExpression patternDispose)
 {
     IsAsync           = isAsync;
     CollectionType    = collectionType;
     GetEnumerator     = getEnumerator;
     MoveNext          = moveNext;
     Current           = current;
     CurrentConversion = currentConversion;
     ElementType       = elementType;
     NeedsDisposal     = needsDisposal;
     DisposeAwaitInfo  = disposeAwaitInfo;
     PatternDispose    = patternDispose;
 }
            internal static WithResources Make(ReadOnlyCollection <ParameterExpression> variables, ReadOnlyCollection <LocalDeclaration> resources, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose)
            {
                RequiresNotNullItems(resources, nameof(resources));
                RequiresNotEmpty(resources, nameof(resources));

                var resourceType = default(Type);

                foreach (var declaration in resources)
                {
                    var declType = declaration.Variable.Type;

                    ValidateType(declType);

                    if (resourceType == null)
                    {
                        resourceType = declType;
                    }
                    else if (resourceType != declType)
                    {
                        // NB: `using (ResourceType r1 = e1, r2 = e2, ...)`.
                        throw Error.UsingVariableDeclarationsShouldBeConsistentlyTyped();
                    }

                    //
                    // REVIEW: This is cumbersome and makes "declaration" a misnomer. It'd likely be better to only use variables for
                    //         additional locals, thus excluding the declared resource variables. The real issue is that we piggyback
                    //         on Roslyn to establish these scopes for locals (e.g. introduced through `out` variables or in patterns
                    //         using `var` or declarations), and here we're getting a union of variables. Our using node is the first
                    //         node to have a first-class notion of "declarations" to reflect the C# grammar.
                    //
                    if (!variables.Contains(declaration.Variable))
                    {
                        throw Error.UsingVariableNotInScope(declaration.Variable);
                    }
                }

                CheckUsingResourceType(resourceType, awaitInfo, patternDispose);

                RequiresCanRead(body, nameof(body));

                return(new WithResources(variables, resources, body, awaitInfo, patternDispose));
            }
 internal WithResources(ReadOnlyCollection <ParameterExpression> variables, ReadOnlyCollection <LocalDeclaration> resources, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose)
     : base(variables, body, awaitInfo, patternDispose)
 {
     Declarations = resources;
 }
            internal static WithResource Make(ReadOnlyCollection <ParameterExpression> variables, Expression resource, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose)
            {
                RequiresCanRead(resource, nameof(resource));

                CheckUsingResourceType(resource.Type, awaitInfo, patternDispose);

                RequiresCanRead(body, nameof(body));

                return(new WithResource(variables, resource, body, awaitInfo, patternDispose));
            }
 internal WithResource(ReadOnlyCollection <ParameterExpression> variables, Expression resource, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose)
     : base(variables, body, awaitInfo, patternDispose)
 {
     Resource = resource;
 }
 /// <summary>
 /// Creates a <see cref="UsingCSharpStatement"/> that represents an await using statement.
 /// </summary>
 /// <param name="awaitInfo">The information required to await the DisposeAsync operation.</param>
 /// <param name="variables">The variables introduced by the statement.</param>
 /// <param name="resource">The resource managed by the statement.</param>
 /// <param name="body">The body of the statement.</param>
 /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns>
 public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, Expression resource, Expression body)
 => AwaitUsing(awaitInfo, variables, resource, body, patternDispose: null);
예제 #15
0
 internal AwaitCSharpExpression(Expression operand, AwaitInfo info)
     : base(operand)
 {
     Info = info;
 }
        internal static ForEachCSharpStatement Make(EnumeratorInfo enumeratorInfo, AwaitInfo awaitInfo, ReadOnlyCollection <ParameterExpression> variables, Expression collection, Expression body, LabelTarget breakLabel, LabelTarget continueLabel, LambdaExpression conversion, LambdaExpression deconstruction)
        {
            if (variables.Count == 0)
            {
                throw Error.ForEachNeedsOneOrMoreVariables();
            }

            RequiresNotNullItems(variables, nameof(variables));

            if (!AreReferenceAssignable(enumeratorInfo.CollectionType, collection.Type))
            {
                throw Error.ForEachCollectionTypeNotCompatibleWithCollectionExpression(enumeratorInfo.CollectionType, collection.Type);
            }

            RequiresCanRead(body, nameof(body));

            ValidateLoop(body, breakLabel, continueLabel);

            var firstVariable     = variables[0];
            var firstVariableType = firstVariable.Type;

            if (variables.Count == 1)
            {
                if (deconstruction != null)
                {
                    throw Error.ForEachDeconstructionNotSupportedWithOneVariable();
                }

                ValidateConversion(firstVariableType, enumeratorInfo.ElementType, ref conversion);
            }
            else
            {
                if (deconstruction == null)
                {
                    throw Error.ForEachDeconstructionRequiredForMultipleVariables();
                }

                ValidateDeconstruction(enumeratorInfo.ElementType, ref conversion, deconstruction, variables);
            }

            if (awaitInfo == null)
            {
                if (collection.Type == typeof(string) && variables.Count == 1 && firstVariableType == typeof(char) && conversion == null && deconstruction == null)
                {
                    return(new StringForEachStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel));
                }
                else if (collection.Type.IsArray)
                {
                    if (collection.Type.IsVector())
                    {
                        if (conversion == null && deconstruction == null)
                        {
                            return(new SimpleArrayForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel));
                        }
                        else
                        {
                            return(new ArrayForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel, conversion, deconstruction));
                        }
                    }
                    else
                    {
                        return(new MultiDimensionalArrayForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel, conversion, deconstruction));
                    }
                }
            }
            else
            {
                awaitInfo.RequiresCanBind(enumeratorInfo.MoveNext.Body);
            }

            return(new BoundForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel, conversion, deconstruction, awaitInfo));
        }
        /// <summary>
        /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will return this expression.
        /// </summary>
        /// <param name="enumeratorInfo">The <see cref="EnumeratorInfo" /> property of the result.</param>
        /// <param name="breakLabel">The <see cref="LoopCSharpStatement.BreakLabel" /> property of the result.</param>
        /// <param name="continueLabel">The <see cref="LoopCSharpStatement.ContinueLabel" /> property of the result.</param>
        /// <param name="variables">The <see cref="Variables" /> property of the result.</param>
        /// <param name="collection">The <see cref="Collection" /> property of the result.</param>
        /// <param name="conversion">The <see cref="Conversion"/> property of the result.</param>
        /// <param name="body">The <see cref="LoopCSharpStatement.Body" /> property of the result.</param>
        /// <param name="deconstruction">The <see cref="Deconstruction"/> property of the result.</param>
        /// <param name="awaitInfo">The <see cref="AwaitInfo"/> property of the result.</param>
        /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
        public ForEachCSharpStatement Update(EnumeratorInfo enumeratorInfo, LabelTarget breakLabel, LabelTarget continueLabel, IEnumerable <ParameterExpression> variables, Expression collection, LambdaExpression conversion, Expression body, LambdaExpression deconstruction, AwaitInfo awaitInfo)
        {
            if (enumeratorInfo == EnumeratorInfo &&
                breakLabel == BreakLabel &&
                continueLabel == ContinueLabel &&
                SameElements(ref variables, Variables) &&
                collection == Collection &&
                conversion == Conversion &&
                body == Body &&
                deconstruction == Deconstruction &&
                awaitInfo == AwaitInfo)
            {
                return(this);
            }

            return(CSharpExpression.ForEach(awaitInfo, variables, collection, body, breakLabel, continueLabel, conversion, deconstruction));
        }
 /// <summary>
 /// Creates a <see cref="UsingCSharpStatement"/> that represents an await using statement.
 /// </summary>
 /// <param name="awaitInfo">The information required to await the DisposeAsync operation.</param>
 /// <param name="resource">The resource managed by the statement.</param>
 /// <param name="body">The body of the statement.</param>
 /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns>
 public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, Expression resource, Expression body) =>
 AwaitUsing(awaitInfo, variables: null, resource, body, patternDispose: null);
 /// <summary>
 /// Creates a <see cref="UsingCSharpStatement"/> that represents an await using statement.
 /// </summary>
 /// <param name="awaitInfo">The information required to await the DisposeAsync operation.</param>
 /// <param name="variables">The variables introduced by the statement.</param>
 /// <param name="declarations">The resources managed by the statement.</param>
 /// <param name="body">The body of the statement.</param>
 /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns>
 public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, IEnumerable <LocalDeclaration> declarations, Expression body)
 => AwaitUsing(awaitInfo, variables, declarations, body, patternDispose: null);
예제 #20
0
 /// <summary>
 /// Creates a new expression that is like this one, but using the supplied children.
 /// </summary>
 /// <param name="operand">The <see cref="UnaryCSharpExpression.Operand" /> property of the result.</param>
 /// <param name="info">The <see cref="AwaitCSharpExpression.Info"/> property of the result.</param>
 /// <returns>An expression with the updated children.</returns>
 protected internal virtual AwaitCSharpExpression Rewrite(Expression operand, AwaitInfo info) => CSharpExpression.Await(operand, info);
예제 #21
0
        /// <summary>
        /// Creates a new <see cref="EnumeratorInfo"/> object providing binding information for foreach operations.
        /// </summary>
        /// <param name="isAsync">A Boolean indicating whether the enumeration is asynchronous.</param>
        /// <param name="collectionType">The type of the collection being enumerated.</param>
        /// <param name="getEnumerator">The <see cref="LambdaExpression"/> representing the operation used to obtain an enumerator from the collection.</param>
        /// <param name="moveNext">The <see cref="LambdaExpression"/> representing the operation used to invoke MoveNext or MoveNextAsync on the enumerator instance.</param>
        /// <param name="current">The Current property on the enumerator.</param>
        /// <param name="currentConversion">The <see cref="LambdaExpression"/> representing the conversion of the object returned from the Current property to the element type.</param>
        /// <param name="elementType">The type of the elements obtained by the iteration.</param>
        /// <param name="needsDisposal">A Boolean indicating whether a call to Dispose or DisposeAsync is required.</param>
        /// <param name="disposeAwaitInfo">The information required to await the DisposeAsync operation for await foreach statements.</param>
        /// <param name="patternDispose">The (optional) <see cref="LambdaExpression"/> representing how to call the dispose method.</param>
        /// <returns>A <see cref="EnumeratorInfo"/> object providing binding information for foreach operations.</returns>
        public static EnumeratorInfo EnumeratorInfo(
            bool isAsync,
            Type collectionType,
            LambdaExpression getEnumerator,
            LambdaExpression moveNext,
            PropertyInfo current,
            LambdaExpression currentConversion,
            Type elementType,
            bool needsDisposal,
            AwaitInfo disposeAwaitInfo,
            LambdaExpression patternDispose)
        {
            RequiresNotNull(collectionType, nameof(collectionType));
            ValidateType(collectionType);

            RequiresCanRead(getEnumerator, nameof(getEnumerator));

            if (getEnumerator.Parameters.Count != 1)
            {
                throw new Exception(); // TODO
            }
            if (!AreReferenceAssignable(getEnumerator.Parameters[0].Type, collectionType))
            {
                throw new Exception(); // TODO
            }
            var enumeratorType = getEnumerator.ReturnType;

            RequiresCanRead(moveNext, nameof(moveNext));

            if (moveNext.Parameters.Count != 1)
            {
                throw new Exception(); // TODO
            }
            if (!AreReferenceAssignable(moveNext.Parameters[0].Type, enumeratorType))
            {
                throw new Exception(); // TODO
            }
            if (!isAsync && moveNext.ReturnType != typeof(bool))
            {
                throw new Exception(); // TOOD
            }
            //
            // REVIEW: We don't have info about the await operation on MoveNextAsync here, so can't validate the return type.
            //
            //         Note we can't call CSharpExpression.Await to attempt to infer an await operation, because GetAwaiter
            //         could come in through an extension method. We'll have the ForEach factory do the final check.
            //

            RequiresNotNull(current, nameof(current));

            var currentGetMethod = current.GetGetMethod(nonPublic: false);

            if (currentGetMethod == null)
            {
                throw new Exception(); // TOOD
            }
            if (currentGetMethod.IsStatic)
            {
                throw new Exception();
            }
            if (current.GetIndexParameters().Length != 0)
            {
                throw new Exception();
            }

            var currentType = current.PropertyType;

            if (currentType == typeof(void))
            {
                throw new Exception(); // TOOD
            }
            if (currentConversion != null)
            {
                RequiresCanRead(currentConversion, nameof(currentConversion));

                if (currentConversion.Parameters.Count != 1)
                {
                    throw new Exception(); // TODO
                }
                if (!AreReferenceAssignable(currentConversion.Parameters[0].Type, currentType))
                {
                    throw new Exception(); // TODO
                }
                if (!AreReferenceAssignable(elementType, currentConversion.ReturnType))
                {
                    throw new Exception(); // TODO
                }
            }
            else
            {
                if (!AreReferenceAssignable(elementType, currentType))
                {
                    throw new Exception(); // TODO
                }
            }

            if (patternDispose != null)
            {
                if (patternDispose.Parameters.Count != 1)
                {
                    throw Error.UsingPatternDisposeShouldHaveOneParameter();
                }
            }

            if (isAsync)
            {
                AssertUsingAwaitInfo(ref disposeAwaitInfo, patternDispose);
            }

            CheckUsingResourceType(enumeratorType, disposeAwaitInfo, patternDispose, allowConvertToDisposable: true);

            return(new EnumeratorInfo(isAsync, collectionType, getEnumerator, moveNext, current, currentConversion, elementType, needsDisposal, disposeAwaitInfo, patternDispose));
        }
        // NB: The Roslyn compiler binds to the overloads below.

        /// <summary>
        /// Creates a <see cref="UsingCSharpStatement"/> that represents a using or an await using statement.
        /// </summary>
        /// <param name="awaitInfo">The information required to await the DisposeAsync operation, or null if not asynchronous.</param>
        /// <param name="variables">The variables introduced by the statement.</param>
        /// <param name="resource">The resource managed by the statement.</param>
        /// <param name="body">The body of the statement.</param>
        /// <param name="patternDispose">The (optional) lambda expression representing how to call the dispose method.</param>
        /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns>
        public static UsingCSharpStatement Using(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, Expression resource, Expression body, LambdaExpression patternDispose)
        {
            RequiresNotNull(resource, nameof(resource));

            return(UsingCSharpStatement.Make(variables, resource, resources: null, body, awaitInfo, patternDispose));
        }
 private static void AssertUsingAwaitInfo(ref AwaitInfo awaitInfo, LambdaExpression patternDispose)
 {
     awaitInfo ??= AwaitInfo(patternDispose?.ReturnType ?? typeof(ValueTask));
 }
        /// <summary>
        /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will return this expression.
        /// </summary>
        /// <param name="variables">The <see cref="Variables" /> property of the result.</param>
        /// <param name="resource">The <see cref="Resource" /> property of the result.</param>
        /// <param name="declarations">The <see cref="Declarations" /> property of the result.</param>
        /// <param name="body">The <see cref="Body" /> property of the result.</param>
        /// <param name="awaitInfo">The <see cref="AwaitInfo"/> property of the result.</param>
        /// <param name="patternDispose">The <see cref="PatternDispose"/> property of the result.</param>
        /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
        public UsingCSharpStatement Update(IEnumerable <ParameterExpression> variables, Expression resource, IEnumerable <LocalDeclaration> declarations, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose)
        {
            if (SameElements(ref variables, Variables) && resource == Resource && SameElements(ref declarations, Declarations) && body == Body && awaitInfo == AwaitInfo && patternDispose == PatternDispose)
            {
                return(this);
            }

            return(Make(variables, resource, declarations, body, awaitInfo, patternDispose));
        }
예제 #25
0
        /// <summary>
        /// Creates a new object that is like this one, but using the supplied children. If all of the children are the same, it will return this object.
        /// </summary>
        /// <param name="getEnumerator">The <see cref="GetEnumerator"/> property of the result.</param>
        /// <param name="moveNext">The <see cref="MoveNext"/> property of the result.</param>
        /// <param name="currentConversion">The <see cref="CurrentConversion"/> property of the result.</param>
        /// <param name="disposeAwaitInfo">The <see cref="DisposeAwaitInfo"/> property of the result.</param>
        /// <param name="patternDispose">The <see cref="PatternDispose"/> property of the result.</param>
        /// <returns>This object if no children changed, or an object with the updated children.</returns>
        public EnumeratorInfo Update(LambdaExpression getEnumerator, LambdaExpression moveNext, LambdaExpression currentConversion, AwaitInfo disposeAwaitInfo, LambdaExpression patternDispose)
        {
            if (getEnumerator == GetEnumerator && moveNext == MoveNext && currentConversion == CurrentConversion && disposeAwaitInfo == DisposeAwaitInfo && patternDispose == PatternDispose)
            {
                return(this);
            }

            return(CSharpExpression.EnumeratorInfo(IsAsync, CollectionType, getEnumerator, moveNext, Current, currentConversion, ElementType, NeedsDisposal, DisposeAwaitInfo, patternDispose));
        }
        /// <summary>
        /// Creates a <see cref="UsingCSharpStatement"/> that represents a using or an await using statement.
        /// </summary>
        /// <param name="awaitInfo">The information required to await the DisposeAsync operation, or null if not asynchronous.</param>
        /// <param name="variables">The variables introduced by the statement.</param>
        /// <param name="declarations">The resources managed by the statement.</param>
        /// <param name="body">The body of the statement.</param>
        /// <param name="patternDispose">The (optional) lambda expression representing how to call the dispose method.</param>
        /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns>
        public static UsingCSharpStatement Using(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, IEnumerable <LocalDeclaration> declarations, Expression body, LambdaExpression patternDispose)
        {
            RequiresNotNull(declarations, nameof(declarations));

            return(UsingCSharpStatement.Make(variables, resource: null, declarations, body, awaitInfo, patternDispose));
        }
        internal static UsingCSharpStatement Make(IEnumerable <ParameterExpression> variables, Expression resource, IEnumerable <LocalDeclaration> resources, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose)
        {
            if (patternDispose != null)
            {
                if (patternDispose.Parameters.Count != 1)
                {
                    throw Error.UsingPatternDisposeShouldHaveOneParameter();
                }
            }

            var variablesList = CheckUniqueVariables(variables, nameof(variables));

            if (resource != null)
            {
                if (resources != null)
                {
                    throw Error.InvalidUsingStatement();
                }

                return(WithResource.Make(variablesList, resource, body, awaitInfo, patternDispose));
            }

            if (resources != null)
            {
                if (resource != null)
                {
                    throw Error.InvalidUsingStatement();
                }

                var resourcesList = resources.ToReadOnly();

                return(WithResources.Make(variablesList, resourcesList, body, awaitInfo, patternDispose));
            }

            throw Error.InvalidUsingStatement();
        }
        internal static void CheckUsingResourceType(Type resourceType, AwaitInfo awaitInfo, LambdaExpression patternDispose, bool allowConvertToDisposable = false)
        {
            ValidateType(resourceType);

            var resourceTypeNonNull = resourceType.GetNonNullableType();

            Type disposeReturnType;

            if (patternDispose != null)
            {
                var patternDisposeInputType = patternDispose.Parameters[0].Type;

                if (!AreReferenceAssignable(patternDisposeInputType, resourceTypeNonNull))
                {
                    throw Error.UsingPatternDisposeInputNotCompatibleWithResource(patternDisposeInputType, resourceTypeNonNull);
                }

                disposeReturnType = patternDispose.ReturnType;
            }
            else
            {
                Type disposableInterface;

                if (awaitInfo != null)
                {
                    disposableInterface = typeof(IAsyncDisposable);
                    disposeReturnType   = typeof(ValueTask);
                }
                else
                {
                    disposableInterface = typeof(IDisposable);
                    disposeReturnType   = typeof(void);
                }

                if (allowConvertToDisposable)
                {
                    // NB: In the case of foreach, we allow for a conversion to a disposable interface to be emitted.
                    //     While this conversion or type check can sometimes be elided at runtime, we call the factory
                    //     here for its side-effect of doing the neccessary checks.
                    _ = Expression.Convert(Expression.Variable(resourceType), disposableInterface);
                }
                else
                {
                    // NB: We don't handle implicit conversions here; the C# compiler can emit a Convert node,
                    //     just like it does for those type of conversions in various other places.
                    if (!disposableInterface.IsAssignableFrom(resourceTypeNonNull))
                    {
                        throw LinqError.ExpressionTypeDoesNotMatchAssignment(resourceTypeNonNull, disposableInterface);
                    }
                }
            }

            if (awaitInfo != null)
            {
                awaitInfo.RequiresCanBind(Expression.Parameter(disposeReturnType));
            }
            else
            {
                if (disposeReturnType != typeof(void))
                {
                    throw Error.UsingDisposeShouldReturnVoid();
                }
            }
        }
 private XNode Visit(AwaitInfo node)
 {
     VisitAwaitInfo(node);
     return(_nodes.Pop());
 }