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); }
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)); }
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)); });
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)); }
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)); });
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)); });
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); }
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); });
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)); }
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)); });
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); }