internal ListCSharpPattern(CSharpObjectPatternInfo info, LambdaExpression lengthAccess, LambdaExpression indexerAccess, ReadOnlyCollection <CSharpPattern> patterns)
     : base(info)
 {
     LengthAccess  = lengthAccess;
     IndexerAccess = indexerAccess;
     Patterns      = patterns;
 }
예제 #2
0
 internal RecursiveCSharpPattern(CSharpObjectPatternInfo info, Type type, MethodInfo deconstructMethod, ReadOnlyCollection <PositionalCSharpSubpattern> deconstruction, ReadOnlyCollection <PropertyCSharpSubpattern> properties)
     : base(info)
 {
     Type = type;
     DeconstructMethod = deconstructMethod;
     Deconstruction    = deconstruction;
     Properties        = properties;
 }
        /// <summary>
        /// Creates a pattern that always matches and assigns the input to a variable.
        /// </summary>
        /// <param name="info">Type information about the pattern.</param>
        /// <returns>A <see cref="DiscardCSharpPattern" /> that represents a pattern that always matches.</returns>
        public static VarCSharpPattern Var(CSharpObjectPatternInfo info)
        {
            RequiresNotNull(info, nameof(info));

            if (info.Info.InputType != info.Info.NarrowedType)
            {
                throw Error.PatternInputAndNarrowedTypeShouldMatch(nameof(CSharpPatternType.Var));
            }
            if (info.Variable != null && info.Variable.Type != info.Info.NarrowedType)
            {
                throw Error.CannotAssignPatternResultToVariable(info.Variable.Type, info.Info.NarrowedType);
            }

            return(new VarCSharpPattern(info));
        }
예제 #4
0
        /// <summary>
        /// Creates a positional pattern that matches on components of an object.
        /// </summary>
        /// <param name="info">Type information about the pattern.</param>
        /// <param name="type">The type to check for.</param>
        /// <param name="deconstructMethod">The method used to deconstruct an object into its components for use with positional subpatterns.</param>
        /// <param name="deconstruction">The property subpatterns to apply.</param>
        /// <returns>A <see cref="IPositionalCSharpPattern" /> representing a positional pattern.</returns>
        public static IPositionalCSharpPattern Positional(CSharpObjectPatternInfo info, Type type, MethodInfo deconstructMethod, IEnumerable <PositionalCSharpSubpattern> deconstruction)
        {
            RequiresNotNull(info, nameof(info));

            if (deconstructMethod == null && info.Variable == null && type == null)
            {
                var narrowedType = info.Info.NarrowedType;

                if (!IsTupleType(narrowedType))
                {
                    return(ITuple(deconstruction));
                }
            }

            return(Recursive(info, type, deconstructMethod, deconstruction, properties: null));
        }
        // REVIEW: Type cannot be nullable.

        /// <summary>
        /// Creates a pattern that checks convertibility to a type and assigns to a variable.
        /// </summary>
        /// <param name="info">Type information about the pattern.</param>
        /// <param name="type">The type to check for.</param>
        /// <returns>A <see cref="DeclarationCSharpPattern" /> that represents a pattern that checks convertibility to a type and assigns to a variable.</returns>
        public static DeclarationCSharpPattern Declaration(CSharpObjectPatternInfo info, Type type)
        {
            RequiresNotNull(info, nameof(info));
            RequiresNotNull(type, nameof(type));

            ValidatePatternType(type);

            var variableType = info.Variable?.Type;

            if (variableType != null)
            {
                RequiresCompatiblePatternTypes(type, variableType);

                if (variableType != info.Info.NarrowedType)
                {
                    throw Error.CannotAssignPatternResultToVariable(variableType, info.Info.NarrowedType);
                }
            }

            return(new DeclarationCSharpPattern(info, type));
        }
        /// <summary>
        /// Creates a list pattern.
        /// </summary>
        /// <param name="info">Type information about the pattern.</param>
        /// <param name="lengthAccess">The <see cref="LambdaExpression"/> representing the expression used to retrieve the collection's length or element count.</param>
        /// <param name="indexerAccess">The <see cref="LambdaExpression"/> representing the indexer access used to retrieve an element from the collection.</param>
        /// <param name="patterns">The list of <see cref="CSharpPattern"/> patterns to apply to the elements of the collection, optionally containing a slice pattern.</param>
        /// <returns>A <see cref="ListCSharpPattern" /> representing a list pattern.</returns>
        public static ListCSharpPattern List(CSharpObjectPatternInfo info, LambdaExpression lengthAccess, LambdaExpression indexerAccess, IEnumerable <CSharpPattern> patterns)
        {
            RequiresNotNull(info, nameof(info));

            var nonNullInputType = info.Info.InputType.GetNonNullableType();
            var collectionType   = info.Info.NarrowedType;

            if (!AreEquivalent(nonNullInputType, collectionType))
            {
                throw Error.ListPatternInputTypeInvalid(nonNullInputType, collectionType);
            }

            RequiresCanRead(lengthAccess, nameof(lengthAccess));

            if (lengthAccess.Parameters.Count != 1)
            {
                throw Error.LengthAccessShouldHaveOneParameter();
            }

            if (!AreEquivalent(lengthAccess.Parameters[0].Type, collectionType))
            {
                throw Error.LengthAccessParameterShouldHaveCollectionType(collectionType);
            }

            if (lengthAccess.ReturnType != typeof(int))
            {
                throw Error.LengthAccessShouldReturnInt32();
            }

            RequiresCanRead(indexerAccess, nameof(indexerAccess));

            if (indexerAccess.Parameters.Count != 2)
            {
                throw Error.IndexerAccessShouldHaveTwoParameters();
            }

            if (!AreEquivalent(indexerAccess.Parameters[0].Type, collectionType))
            {
                throw Error.IndexerAccessFirstParameterShouldHaveCollectionType(collectionType);
            }

            if (indexerAccess.Parameters[1].Type != typeof(Index))
            {
                throw Error.IndexerAccessSecondParameterInvalidType(typeof(Index));
            }

            var elementType = indexerAccess.ReturnType;

            if (elementType == typeof(void))
            {
                throw Error.ElementTypeCannotBeVoid();
            }

            var patternsList = patterns.ToArray();

            RequiresNotNullItems(patternsList, nameof(patterns));

            var hasSlice = false;

            for (int i = 0, n = patternsList.Length; i < n; i++)
            {
                var pattern = patternsList[i];

                if (pattern.PatternType == CSharpPatternType.Slice)
                {
                    if (hasSlice)
                    {
                        throw Error.MoreThanOneSlicePattern();
                    }

                    hasSlice = true;

                    RequiresCompatiblePatternTypes(collectionType, ref patternsList[i]);
                }
                else
                {
                    RequiresCompatiblePatternTypes(elementType, ref patternsList[i]);
                }
            }

            return(new ListCSharpPattern(info, lengthAccess, indexerAccess, new TrueReadOnlyCollection <CSharpPattern>(patternsList)));
        }
 /// <summary>
 /// Creates a list pattern.
 /// </summary>
 /// <param name="info">Type information about the pattern.</param>
 /// <param name="lengthAccess">The <see cref="LambdaExpression"/> representing the expression used to retrieve the collection's length or element count.</param>
 /// <param name="indexerAccess">The <see cref="LambdaExpression"/> representing the indexer access used to retrieve an element from the collection.</param>
 /// <param name="patterns">The list of <see cref="CSharpPattern"/> patterns to apply to the elements of the collection, optionally containing a slice pattern.</param>
 /// <returns>A <see cref="ListCSharpPattern" /> representing a list pattern.</returns>
 public static ListCSharpPattern List(CSharpObjectPatternInfo info, LambdaExpression lengthAccess, LambdaExpression indexerAccess, params CSharpPattern[] patterns) =>
 List(info, lengthAccess, indexerAccess, (IEnumerable <CSharpPattern>)patterns);
예제 #8
0
        /// <summary>
        /// Creates a recursive pattern that can perform positional matching and/or matching on properties.
        /// </summary>
        /// <param name="info">Type information about the pattern.</param>
        /// <param name="type">The type to check for.</param>
        /// <param name="deconstructMethod">The method used to deconstruct an object into its components for use with positional subpatterns.</param>
        /// <param name="deconstruction">The positional subpatterns to apply to the components of the object.</param>
        /// <param name="properties">The property subpatterns to apply.</param>
        /// <returns>A <see cref="RecursiveCSharpPattern" /> representing a recursive pattern.</returns>
        public static RecursiveCSharpPattern Recursive(CSharpObjectPatternInfo info, Type type, MethodInfo deconstructMethod, IEnumerable <PositionalCSharpSubpattern> deconstruction, IEnumerable <PropertyCSharpSubpattern> properties)
        {
            if (info == null)
            {
                // NB: We could arguably attempt to infer the type from the other parameters but that's a bit too implicit.

                RequiresNotNull(type, nameof(type));

                info = ObjectPatternInfo(PatternInfo(typeof(object), type), variable: null);
            }
            else
            {
                if (info.Variable != null)
                {
                    RequiresCompatiblePatternTypes(info.Info.NarrowedType, info.Variable.Type);
                }
            }

            var narrowedType             = info.Info.NarrowedType;
            var deconstructionCollection = deconstruction.ToReadOnly();
            var propertiesCollection     = properties.ToReadOnly();

            var objParam = Expression.Parameter(narrowedType); // NB: Utility to utilize some expression factories to expedite some checks.

            // REVIEW: Reordering of positional matches with names (for tuple elements or deconstruction out parameters) is not supported.

            validateType();
            validatePositional();
            validateProperties();

            return(new RecursiveCSharpPattern(info, type, deconstructMethod, deconstructionCollection, propertiesCollection));

            void validateType()
            {
                if (type != null)
                {
                    ValidatePatternType(type);

                    var variableType = info.Variable?.Type;

                    if (variableType != null)
                    {
                        RequiresCompatiblePatternTypes(type, variableType);

                        if (variableType != narrowedType)
                        {
                            throw Error.CannotAssignPatternResultToVariable(variableType, narrowedType);
                        }
                    }
                }
            }

            void validatePositional()
            {
                if (deconstructionCollection.Count > 0)
                {
                    foreach (var positionalPattern in deconstructionCollection)
                    {
                        RequiresNotNull(positionalPattern, nameof(deconstruction));
                    }

                    if (deconstructMethod != null)
                    {
                        validateWithDeconstructMethod();
                    }
                    else if (IsTupleType(narrowedType))
                    {
                        validateWithTupleType();
                    }
                    else
                    {
                        throw Error.InvalidPositionalPattern();
                    }

                    void validateWithDeconstructMethod()
                    {
                        ValidateMethodInfo(deconstructMethod);

                        if (deconstructMethod.ReturnType != typeof(void))
                        {
                            throw Error.DeconstructShouldReturnVoid(deconstructMethod);
                        }

                        var parameters = deconstructMethod.GetParametersCached();

                        if (deconstructMethod.IsStatic)
                        {
                            if (parameters.Length == 0)
                            {
                                throw Error.DeconstructExtensionMethodMissingThis(deconstructMethod);
                            }

                            ValidateOneArgument(deconstructMethod, ExpressionType.Call, objParam, parameters[0]);

                            parameters = parameters.RemoveFirst();
                        }
                        else
                        {
                            ValidateCallInstanceType(narrowedType, deconstructMethod);
                        }

                        var arity = parameters.Length;

                        checkArity(arity);

                        var parameterToPattern = new Dictionary <ParameterInfo, PositionalCSharpSubpattern>();

                        foreach (var positionalPattern in deconstructionCollection)
                        {
                            if (positionalPattern.Field != null)
                            {
                                throw Error.PositionalPatternWithDeconstructMethodCannotSpecifyField();
                            }

                            var parameter = positionalPattern.Parameter;

                            if (parameter != null)
                            {
                                if (parameter.Member != deconstructMethod)
                                {
                                    throw Error.PositionalPatternParameterIsNotDeclaredOnDeconstructMethod(parameter, deconstructMethod);
                                }

                                if (parameterToPattern.ContainsKey(parameter))
                                {
                                    throw Error.PositionalPatternParameterShouldOnlyBeUsedOnce(parameter);
                                }

                                parameterToPattern.Add(parameter, positionalPattern);
                            }
                        }

                        var bindByParameter = parameterToPattern.Count > 0;

                        if (bindByParameter && parameterToPattern.Count != arity)
                        {
                            throw Error.PositionalPatternWithDeconstructMethodShouldSpecifyAllParameters();
                        }

                        PositionalCSharpSubpattern getPositionalPattern(ParameterInfo parameter, int index) => bindByParameter
                            ? parameterToPattern[parameter]
                            : deconstructionCollection[index];

                        for (var i = 0; i < arity; i++)
                        {
                            var parameter = parameters[i];

                            if (!parameter.IsOut)
                            {
                                throw Error.DeconstructParameterShouldBeOut(parameter, deconstructMethod);
                            }

                            var pattern       = getPositionalPattern(parameter, i).Pattern;
                            var parameterType = parameter.ParameterType.GetElementType();

                            // REVIEW: Can we loosen the type checking here (assignment compatibility) or trigger ChangeType?
                            RequiresCompatiblePatternTypes(pattern.InputType, parameterType);
                        }
                    }

                    void validateWithTupleType()
                    {
                        var arity = GetTupleArity(narrowedType);

                        checkArity(arity);

                        var byIndexCount   = 0;
                        var indexToPattern = new PositionalCSharpSubpattern[arity];

                        foreach (var positionalPattern in deconstructionCollection)
                        {
                            if (positionalPattern.Parameter != null)
                            {
                                throw Error.PositionalPatternWithTupleCannotSpecifyParameter();
                            }

                            if (positionalPattern.Field != null)
                            {
                                var index = positionalPattern.Field.Index;

                                if (index < 0 || index >= arity)
                                {
                                    throw Error.PositionalPatternTupleIndexOutOfRange(index, arity);
                                }

                                if (indexToPattern[index] != null)
                                {
                                    throw Error.PositionalPatternTupleIndexShouldOnlyBeUsedOnce(index);
                                }

                                indexToPattern[index] = positionalPattern;
                                byIndexCount++;
                            }
                        }

                        var bindByIndex = byIndexCount > 0;

                        if (bindByIndex && byIndexCount != arity)
                        {
                            throw Error.PositionalPatternWithTupleShouldSpecifyAllIndices();
                        }

                        PositionalCSharpSubpattern getPositionalPattern(int index) => bindByIndex
                            ? indexToPattern[index]
                            : deconstructionCollection[index];

                        var elementTypes = GetTupleComponentTypes(narrowedType).ToReadOnly();

                        for (var i = 0; i < arity; i++)
                        {
                            var elementType = elementTypes[i];

                            var pattern = getPositionalPattern(i).Pattern;

                            // REVIEW: Can we loosen the type checking here (assignment compatibility) or trigger ChangeType?
                            RequiresCompatiblePatternTypes(pattern.InputType, elementType);
                        }
                    }

                    void checkArity(int arity)
                    {
                        if (arity != deconstructionCollection.Count)
                        {
                            throw Error.InvalidPositionalPatternCount(narrowedType);
                        }
                    }
                }
            }

            void validateProperties()
            {
                foreach (var propertyPattern in propertiesCollection)
                {
                    RequiresNotNull(propertyPattern, nameof(properties));

                    var member = findTopMostMember(propertyPattern.Member);

                    if (member.Member != null)
                    {
                        // TODO: Inline the check.
                        _ = Expression.MakeMemberAccess(objParam, member.Member);
                    }
                    else
                    {
                        // TODO: Inline the check.
                        _ = Helpers.GetTupleItemAccess(objParam, member.TupleField.Index);
                    }
                }
예제 #9
0
 /// <summary>
 /// Creates a property pattern that matches on properties.
 /// </summary>
 /// <param name="info">Type information about the pattern.</param>
 /// <param name="type">The type to check for.</param>
 /// <param name="properties">The property subpatterns to apply.</param>
 /// <returns>A <see cref="RecursiveCSharpPattern" /> representing a property pattern.</returns>
 public static RecursiveCSharpPattern Property(CSharpObjectPatternInfo info, Type type, IEnumerable <PropertyCSharpSubpattern> properties) =>
 Recursive(info, type, deconstructMethod: null, deconstruction: null, properties);
예제 #10
0
 /// <summary>
 /// Creates a property pattern that matches on properties.
 /// </summary>
 /// <param name="info">Type information about the pattern.</param>
 /// <param name="type">The type to check for.</param>
 /// <param name="properties">The property subpatterns to apply.</param>
 /// <returns>A <see cref="RecursiveCSharpPattern" /> representing a property pattern.</returns>
 public static RecursiveCSharpPattern Property(CSharpObjectPatternInfo info, Type type, params PropertyCSharpSubpattern[] properties) =>
 Property(info, type, (IEnumerable <PropertyCSharpSubpattern>)properties);
예제 #11
0
        //
        // TODO: Add more convenience overloads for positional recursive patterns.
        //
        //       - Should it find Deconstruct instance methods if unspecified?
        //         - This will require to find types from PositionalCSharpSubpattern.Pattern.InputType to do overload resolution for the out parameters.
        //

        /// <summary>
        /// Creates a positional pattern that matches on components of an object.
        /// </summary>
        /// <param name="info">Type information about the pattern.</param>
        /// <param name="type">The type to check for.</param>
        /// <param name="deconstructMethod">The method used to deconstruct an object into its components for use with positional subpatterns.</param>
        /// <param name="deconstruction">The property subpatterns to apply.</param>
        /// <returns>A <see cref="IPositionalCSharpPattern" /> representing a positional pattern.</returns>
        public static IPositionalCSharpPattern Positional(CSharpObjectPatternInfo info, Type type, MethodInfo deconstructMethod, params PositionalCSharpSubpattern[] deconstruction) => Positional(info, type, deconstructMethod, (IEnumerable <PositionalCSharpSubpattern>)deconstruction);
예제 #12
0
 internal CSharpObjectPattern(CSharpObjectPatternInfo info)
     : base(info.Info)
 {
     _objectInfo = info;
 }
 internal VarCSharpPattern(CSharpObjectPatternInfo info)
     : base(info)
 {
 }
 internal DeclarationCSharpPattern(CSharpObjectPatternInfo info, Type type)
     : base(info)
 {
     Type = type;
 }