Example #1
0
        private static TypeSymbol NarrowArrayAssignmentType(ITypeManager typeManager, ArraySyntax expression, ArrayType targetType, IDiagnosticWriter diagnosticWriter, bool skipConstantCheck)
        {
            // if we have parse errors, no need to check assignability
            // we should not return the parse errors however because they will get double collected
            if (expression.HasParseErrors())
            {
                return(targetType);
            }

            var arrayProperties = new List <TypeSymbol>();

            foreach (var arrayItemSyntax in expression.Items)
            {
                arrayProperties.Add(NarrowTypeInternal(
                                        typeManager,
                                        arrayItemSyntax.Value,
                                        targetType.Item.Type,
                                        diagnosticWriter,
                                        (expectedType, actualType, errorExpression) => DiagnosticBuilder.ForPosition(errorExpression).ArrayTypeMismatch(ShouldWarn(targetType), expectedType, actualType),
                                        skipConstantCheck,
                                        skipTypeErrors: true));
            }

            return(new TypedArrayType(UnionType.Create(arrayProperties), targetType.ValidationFlags));
        }
        public DiscriminatedObjectType(string name, TypeSymbolValidationFlags validationFlags, string discriminatorKey, IEnumerable <ITypeReference> unionMembers)
            : base(name)
        {
            var unionMembersByKey = new Dictionary <string, ITypeReference>();
            var unionKeyTypes     = new List <StringLiteralType>();

            foreach (var member in unionMembers)
            {
                if (!(member.Type is NamedObjectType namedObject))
                {
                    throw new ArgumentException($"Invalid member of type {member.Type.GetType()}");
                }

                if (!namedObject.Properties.TryGetValue(discriminatorKey, out var discriminatorProp))
                {
                    throw new ArgumentException("Missing discriminator field on member");
                }

                if (!(discriminatorProp.TypeReference.Type is StringLiteralType stringLiteral))
                {
                    throw new ArgumentException($"Invalid discriminator field type {discriminatorProp.TypeReference.Type.Name} on member");
                }

                unionMembersByKey.Add(stringLiteral.Name, member);
                unionKeyTypes.Add(stringLiteral);
            }

            this.UnionMembersByKey          = unionMembersByKey.ToImmutableDictionary();
            this.ValidationFlags            = validationFlags;
            this.DiscriminatorKey           = discriminatorKey;
            this.DiscriminatorKeysUnionType = UnionType.Create(unionKeyTypes);
        }
Example #3
0
        private TypeSymbol GetTernaryOperationType(TypeManagerContext context, TernaryOperationSyntax syntax)
        {
            var errors = new List <ErrorDiagnostic>();

            // ternary operator requires the condition to be of bool type
            var conditionType = this.GetTypeInfoInternal(context, syntax.ConditionExpression);

            CollectErrors(errors, conditionType);

            var trueType = this.GetTypeInfoInternal(context, syntax.TrueExpression);

            CollectErrors(errors, trueType);

            var falseType = this.GetTypeInfoInternal(context, syntax.FalseExpression);

            CollectErrors(errors, falseType);

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            var expectedConditionType = LanguageConstants.Bool;

            if (TypeValidator.AreTypesAssignable(conditionType, expectedConditionType) != true)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.ConditionExpression).ValueTypeMismatch(expectedConditionType.Name)));
            }

            // the return type is the union of true and false expression types
            return(UnionType.Create(trueType, falseType));
        }
Example #4
0
        public override void VisitTernaryOperationSyntax(TernaryOperationSyntax syntax)
        => AssignTypeWithCaching(syntax, () => {
            var errors = new List <ErrorDiagnostic>();

            // ternary operator requires the condition to be of bool type
            var conditionType = VisitAndReturnType(syntax.ConditionExpression);
            CollectErrors(errors, conditionType);

            var trueType = VisitAndReturnType(syntax.TrueExpression);
            CollectErrors(errors, trueType);

            var falseType = VisitAndReturnType(syntax.FalseExpression);
            CollectErrors(errors, falseType);

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            var expectedConditionType = LanguageConstants.Bool;
            if (TypeValidator.AreTypesAssignable(conditionType, expectedConditionType) != true)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.ConditionExpression).ValueTypeMismatch(expectedConditionType.Name)));
            }

            // the return type is the union of true and false expression types
            return(UnionType.Create(trueType, falseType));
        });
Example #5
0
        private TypeSymbol GetArrayType(TypeManagerContext context, ArraySyntax array)
        {
            var errors = new List <ErrorDiagnostic>();

            var itemTypes = new List <TypeSymbol>(array.Children.Length);

            foreach (SyntaxBase arrayItem in array.Children)
            {
                var itemType = this.GetTypeInfoInternal(context, arrayItem);
                itemTypes.Add(itemType);
                CollectErrors(errors, itemType);
            }

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            var aggregatedItemType = UnionType.Create(itemTypes);

            if (aggregatedItemType.TypeKind == TypeKind.Union || aggregatedItemType.TypeKind == TypeKind.Never)
            {
                // array contains a mix of item types or is empty
                // assume array of any for now
                return(LanguageConstants.Array);
            }

            return(new TypedArrayType(aggregatedItemType));
        }
Example #6
0
        public override void VisitObjectSyntax(ObjectSyntax syntax)
        => AssignType(syntax, () => {
            var errors = new List <ErrorDiagnostic>();

            foreach (var objectProperty in syntax.Properties)
            {
                var propertyType = typeManager.GetTypeInfo(objectProperty);
                CollectErrors(errors, propertyType);
            }

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            // type results are cached
            var namedProperties = syntax.Properties
                                  .GroupByExcludingNull(p => p.TryGetKeyText(), LanguageConstants.IdentifierComparer)
                                  .Select(group => new TypeProperty(group.Key, UnionType.Create(group.Select(p => typeManager.GetTypeInfo(p)))));

            var additionalProperties = syntax.Properties
                                       .Where(p => p.TryGetKeyText() == null)
                                       .Select(p => typeManager.GetTypeInfo(p));

            var additionalPropertiesType = additionalProperties.Any() ? UnionType.Create(additionalProperties) : null;

            // TODO: Add structural naming?
            return(new NamedObjectType(LanguageConstants.Object.Name, namedProperties, additionalPropertiesType));
        });
Example #7
0
        public override void VisitArraySyntax(ArraySyntax syntax)
        => AssignTypeWithCaching(syntax, () => {
            var errors = new List <ErrorDiagnostic>();

            var itemTypes = new List <TypeSymbol>(syntax.Children.Length);
            foreach (var arrayItem in syntax.Children)
            {
                var itemType = VisitAndReturnType(arrayItem);
                itemTypes.Add(itemType);
                CollectErrors(errors, itemType);
            }

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            var aggregatedItemType = UnionType.Create(itemTypes);
            if (aggregatedItemType.TypeKind == TypeKind.Union || aggregatedItemType.TypeKind == TypeKind.Never)
            {
                // array contains a mix of item types or is empty
                // assume array of any for now
                return(LanguageConstants.Array);
            }

            return(new TypedArrayType(aggregatedItemType));
        });
Example #8
0
        private static TypeSymbol NarrowTypeInternal(ITypeManager typeManager,
                                                     SyntaxBase expression,
                                                     TypeSymbol targetType,
                                                     IList <Diagnostic> diagnostics,
                                                     TypeMismatchErrorFactory typeMismatchErrorFactory,
                                                     bool skipConstantCheck,
                                                     bool skipTypeErrors)
        {
            if (targetType is ResourceType targetResourceType)
            {
                // When assigning a resource, we're really assigning the value of the resource body.
                var narrowedBody = NarrowTypeInternal(typeManager, expression, targetResourceType.Body.Type, diagnostics, typeMismatchErrorFactory, skipConstantCheck, skipTypeErrors);

                return(new ResourceType(targetResourceType.TypeReference, narrowedBody, targetResourceType.ValidationFlags));
            }

            // TODO: The type of this expression and all subexpressions should be cached
            TypeSymbol?expressionType = typeManager.GetTypeInfo(expression);

            // since we dynamically checked type, we need to collect the errors but only if the caller wants them
            if (skipTypeErrors == false && expressionType is ErrorTypeSymbol)
            {
                diagnostics.AddRange(expressionType.GetDiagnostics());
            }

            // basic assignability check
            if (AreTypesAssignable(expressionType, targetType) == false)
            {
                // fundamentally different types - cannot assign
                diagnostics.Add(typeMismatchErrorFactory(targetType, expressionType, expression));
                return(targetType);
            }

            // object assignability check
            if (expression is ObjectSyntax objectValue && targetType is ObjectType targetObjectType)
            {
                return(NarrowObjectType(typeManager, objectValue, targetObjectType, diagnostics, skipConstantCheck));
            }

            if (expression is ObjectSyntax objectDiscriminated && targetType is DiscriminatedObjectType targetDiscriminated)
            {
                return(NarrowDiscriminatedObjectType(typeManager, objectDiscriminated, targetDiscriminated, diagnostics, skipConstantCheck));
            }

            // array assignability check
            if (expression is ArraySyntax arrayValue && targetType is ArrayType targetArrayType)
            {
                return(NarrowArrayAssignmentType(typeManager, arrayValue, targetArrayType, diagnostics, skipConstantCheck));
            }

            if (targetType is UnionType targetUnionType)
            {
                return(UnionType.Create(targetUnionType.Members.Where(x => AreTypesAssignable(expressionType, x.Type) == true)));
            }

            return(targetType);
        }
Example #9
0
        public override void VisitParameterDeclarationSyntax(ParameterDeclarationSyntax syntax)
        => AssignTypeWithDiagnostics(syntax, diagnostics => {
            diagnostics.AddRange(this.ValidateIdentifierAccess(syntax));

            // assume "any" type when the parameter has parse errors (either missing or was skipped)
            var declaredType = syntax.ParameterType == null
                    ? LanguageConstants.Any
                    : LanguageConstants.TryGetDeclarationType(syntax.ParameterType.TypeName);

            if (declaredType == null)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Type).InvalidParameterType()));
            }

            var assignedType = declaredType;
            if (object.ReferenceEquals(assignedType, LanguageConstants.String))
            {
                var allowedItemTypes = SyntaxHelper.TryGetAllowedItems(syntax)?
                                       .Select(item => typeManager.GetTypeInfo(item));

                if (allowedItemTypes != null && allowedItemTypes.All(itemType => itemType is StringLiteralType))
                {
                    assignedType = UnionType.Create(allowedItemTypes);
                }
            }

            switch (syntax.Modifier)
            {
            case ParameterDefaultValueSyntax defaultValueSyntax:
                diagnostics.AddRange(ValidateDefaultValue(defaultValueSyntax, assignedType));
                break;

            case ObjectSyntax modifierSyntax:
                var modifierType = LanguageConstants.CreateParameterModifierType(declaredType, assignedType);
                // we don't need to actually use the narrowed type; just need to use this to collect assignment diagnostics
                TypeValidator.NarrowTypeAndCollectDiagnostics(typeManager, modifierSyntax, modifierType, diagnostics);
                break;
            }

            return(assignedType);
        });
Example #10
0
        private TypeSymbol GetObjectType(TypeManagerContext context, ObjectSyntax @object)
        {
            var errors = new List <ErrorDiagnostic>();

            foreach (ObjectPropertySyntax objectProperty in @object.Properties)
            {
                var propertyType = this.GetTypeInfoInternal(context, objectProperty);
                CollectErrors(errors, propertyType);
            }

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            // type results are cached
            var properties = @object.Properties
                             .GroupBy(p => p.GetKeyText(), LanguageConstants.IdentifierComparer)
                             .Select(group => new TypeProperty(group.Key, UnionType.Create(group.Select(p => this.GetTypeInfoInternal(context, p.Value)))));

            // TODO: Add structural naming?
            return(new NamedObjectType(LanguageConstants.Object.Name, properties, additionalPropertiesType: null));
        }
Example #11
0
        public override void VisitObjectSyntax(ObjectSyntax syntax)
        => AssignTypeWithCaching(syntax, () => {
            var errors = new List <ErrorDiagnostic>();

            foreach (var objectProperty in syntax.Properties)
            {
                var propertyType = VisitAndReturnType(objectProperty);
                CollectErrors(errors, propertyType);
            }

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            // type results are cached
            var properties = syntax.Properties
                             .GroupBy(p => p.GetKeyText(), LanguageConstants.IdentifierComparer)
                             .Select(group => new TypeProperty(group.Key, UnionType.Create(group.Select(p => assignedTypes[p]))));

            // TODO: Add structural naming?
            return(new NamedObjectType(LanguageConstants.Object.Name, properties, additionalPropertiesType: null));
        });
Example #12
0
        public override void VisitParameterDeclarationSyntax(ParameterDeclarationSyntax syntax)
        => AssignTypeWithCaching(syntax, () => {
            var primitiveType = LanguageConstants.TryGetDeclarationType(syntax.Type.TypeName);
            if (primitiveType == null)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Type).InvalidParameterType()));
            }

            if (!object.ReferenceEquals(primitiveType, LanguageConstants.String))
            {
                return(primitiveType);
            }

            var allowedItemTypes = SyntaxHelper.TryGetAllowedItems(syntax)?
                                   .Select(item => VisitAndReturnType(item));

            if (allowedItemTypes == null || !allowedItemTypes.All(itemType => itemType is StringLiteralType))
            {
                return(primitiveType);
            }

            return(UnionType.Create(allowedItemTypes));
        });
        public DiscriminatedObjectType(string name, TypeSymbolValidationFlags validationFlags, string discriminatorKey, IEnumerable <ITypeReference> unionMembers)
            : base(name)
        {
            var unionMembersByKey = new Dictionary <string, ObjectType>();
            var unionKeyTypes     = new List <StringLiteralType>();

            // start with required and we will aggregate in everything else
            var discriminatorPropertyFlags = TypePropertyFlags.Required;

            foreach (var member in unionMembers)
            {
                if (member.Type is not ObjectType objectType)
                {
                    throw new ArgumentException($"Invalid member of type {member.Type.GetType()}");
                }

                if (!objectType.Properties.TryGetValue(discriminatorKey, out var discriminatorProp))
                {
                    throw new ArgumentException("Missing discriminator field on member");
                }

                discriminatorPropertyFlags |= discriminatorProp.Flags;

                if (discriminatorProp.TypeReference.Type is not StringLiteralType stringLiteral)
                {
                    throw new ArgumentException($"Invalid discriminator field type {discriminatorProp.TypeReference.Type.Name} on member");
                }

                unionMembersByKey.Add(stringLiteral.Name, objectType);
                unionKeyTypes.Add(stringLiteral);
            }

            this.UnionMembersByKey          = unionMembersByKey.ToImmutableDictionary();
            this.ValidationFlags            = validationFlags;
            this.DiscriminatorKeysUnionType = UnionType.Create(unionKeyTypes);
            this.DiscriminatorProperty      = new TypeProperty(discriminatorKey, this.DiscriminatorKeysUnionType, discriminatorPropertyFlags);
        }