File: BoundTree\BoundCollectionExpression.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    internal partial class BoundCollectionExpression
    {
        private partial void Validate()
        {
            var collectionCreation = this.GetUnconvertedCollectionCreation();
            var collectionBuilderElementsPlaceholder = this.CollectionBuilderElementsPlaceholder;
 
            if (collectionCreation is BoundCall boundCall)
            {
                Debug.Assert(this.CollectionTypeKind == CollectionExpressionTypeKind.CollectionBuilder);
                Debug.Assert(this.CollectionCreation is not null);
                Debug.Assert(this.CollectionBuilderMethod is not null);
                Debug.Assert(collectionBuilderElementsPlaceholder is not null);
                Debug.Assert(collectionBuilderElementsPlaceholder.Type is not null);
 
                Debug.Assert(boundCall.Arguments is [.., BoundCollectionBuilderElementsPlaceholder placeholder] &&
                    placeholder == collectionBuilderElementsPlaceholder);
                Debug.Assert(collectionBuilderElementsPlaceholder.Type.IsReadOnlySpan());
            }
            else
            {
                Debug.Assert(this.CollectionTypeKind != CollectionExpressionTypeKind.CollectionBuilder);
                Debug.Assert(this.CollectionBuilderMethod is null);
                Debug.Assert(collectionBuilderElementsPlaceholder is null);
            }
        }
 
        /// <summary>
        /// Returns <see cref="CollectionCreation"/> with any outer <see cref="BoundConversion"/> nodes unwrapped. The
        /// final returned <see cref="BoundExpression"/> will either be:
        /// <list type="bullet">
        /// <item><see langword="null"/> (when no <c>with(...)</c> element is present),</item>
        /// <item>a <see cref="BoundObjectCreationExpression"/> (when targeting a collection with a constructor),</item>
        /// <item>a <see cref="BoundCall"/> (when targeting a CollectionBuilder method),</item>
        /// <item>a <see cref="BoundNewT"/> (when targeting a type parameter with the <c>new()</c> constraint,</item>
        /// <item>or a <see cref="BoundBadExpression"/> in the case of errors where the <c>with(...)</c> element does
        /// not bind properly.</item>
        /// </list>
        /// </summary>
        public BoundExpression? GetUnconvertedCollectionCreation()
        {
            var collectionCreation = this.CollectionCreation;
            while (collectionCreation is BoundConversion conversion)
                collectionCreation = conversion.Operand;
 
            Debug.Assert(collectionCreation
                is null
                or BoundObjectCreationExpression
                or BoundCall
                or BoundNewT
                or BoundBadExpression);
 
            return collectionCreation;
        }
    }
 
    internal partial class BoundCollectionExpressionBase
    {
        private partial void Validate()
        {
            foreach (var element in Elements)
                Debug.Assert(element is not BoundUnconvertedWithElement);
        }
 
        /// <summary>
        /// Returns true if the collection expression contains any spreads.
        /// </summary>
        /// <param name="numberIncludingLastSpread">The number of elements up to and including the
        /// last spread element. If the length of the collection expression is known, this is the number
        /// of elements evaluated before any are added to the collection instance in lowering.</param>
        /// <param name="hasKnownLength"><see langword="true"/> if all the spread elements are countable.</param>
        internal bool HasSpreadElements(out int numberIncludingLastSpread, out bool hasKnownLength)
        {
            hasKnownLength = true;
            numberIncludingLastSpread = 0;
            for (int i = 0; i < Elements.Length; i++)
            {
                if (Elements[i] is BoundCollectionExpressionSpreadElement spreadElement)
                {
                    numberIncludingLastSpread = i + 1;
                    if (spreadElement.LengthOrCount is null)
                    {
                        hasKnownLength = false;
                    }
                }
            }
            return numberIncludingLastSpread > 0;
        }
 
        public new bool IsParamsArrayOrCollection
        {
            get
            {
                return base.IsParamsArrayOrCollection;
            }
            init
            {
                base.IsParamsArrayOrCollection = value;
            }
        }
    }
}