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)); }
private DeclaredTypeAssignment?GetArrayAccessType(ArrayAccessSyntax syntax) { var baseExpressionAssignment = GetDeclaredTypeAssignment(syntax.BaseExpression); var indexAssignedType = this.typeManager.GetTypeInfo(syntax.IndexExpression); // TODO: Currently array access is broken with discriminated object types - revisit when that is fixed switch (baseExpressionAssignment?.Reference.Type) { case ArrayType arrayType when TypeValidator.AreTypesAssignable(indexAssignedType, LanguageConstants.Int): // we are accessing an array by an expression of a numeric type // return the item type of the array // TODO: We can flow the syntax nodes through declared type assignments if we are able to evaluate the array index - skipping for now return(new DeclaredTypeAssignment(arrayType.Item.Type, declaringSyntax: null)); case ObjectType objectType when syntax.IndexExpression is StringSyntax potentialLiteralValue && potentialLiteralValue.TryGetLiteralValue() is { } propertyName: // string literal indexing over an object is the same as dot property access // it's ok to rely on useSyntax=true because those types have already been established return(this.GetObjectPropertyType( objectType, baseExpressionAssignment.DeclaringSyntax as ObjectSyntax, propertyName, useSyntax: true)); } return(null); }
private TypeSymbol GetUnaryOperationType(TypeManagerContext context, UnaryOperationSyntax syntax) { var errors = new List <ErrorDiagnostic>(); // TODO: When we add number type, this will have to be adjusted var expectedOperandType = syntax.Operator switch { UnaryOperator.Not => LanguageConstants.Bool, UnaryOperator.Minus => LanguageConstants.Int, _ => throw new NotImplementedException() }; var operandType = this.GetTypeInfoInternal(context, syntax.Expression); CollectErrors(errors, operandType); if (errors.Any()) { return(new ErrorTypeSymbol(errors)); } if (TypeValidator.AreTypesAssignable(operandType, expectedOperandType) != true) { return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax).UnaryOperatorInvalidType(Operators.UnaryOperatorToText[syntax.Operator], operandType.Name))); } return(expectedOperandType); }
private TypeSymbol GetPropertyAccessType(TypeManagerContext context, PropertyAccessSyntax syntax) { var errors = new List <ErrorDiagnostic>(); var baseType = this.GetTypeInfoInternal(context, syntax.BaseExpression); CollectErrors(errors, baseType); if (errors.Any()) { return(new ErrorTypeSymbol(errors)); } if (TypeValidator.AreTypesAssignable(baseType, LanguageConstants.Object) != true) { // can only access properties of objects return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.PropertyName).ObjectRequiredForPropertyAccess(baseType))); } if (baseType.TypeKind == TypeKind.Any || !(baseType is ObjectType objectType)) { return(LanguageConstants.Any); } return(this.GetNamedPropertyType(objectType, syntax.PropertyName, syntax.PropertyName.IdentifierName)); }
public static IEnumerable <BinaryOperatorInfo> GetMatches(BinaryOperator @operator, TypeSymbol operandType1, TypeSymbol operandType2) { // error types will cause this to return multiple operator matches in some cases return(OperatorLookup[@operator] .Where(info => TypeValidator.AreTypesAssignable(operandType1, info.OperandType) != false && TypeValidator.AreTypesAssignable(operandType2, info.OperandType) != false)); }
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 IEnumerable <Diagnostic> ValidateDefaultValue(ParameterDefaultValueSyntax defaultValueSyntax, TypeSymbol assignedType) { // figure out type of the default value var defaultValueType = typeManager.GetTypeInfo(defaultValueSyntax.DefaultValue); // this type is not a property in a symbol so the semantic error visitor won't collect the errors automatically if (defaultValueType is ErrorTypeSymbol) { return(defaultValueType.GetDiagnostics()); } if (TypeValidator.AreTypesAssignable(defaultValueType, assignedType) == false) { return(DiagnosticBuilder.ForPosition(defaultValueSyntax.DefaultValue).ParameterTypeMismatch(assignedType.Name, defaultValueType.Name).AsEnumerable()); } return(Enumerable.Empty <Diagnostic>()); }
private IEnumerable <Diagnostic> GetOutputDeclarationDiagnostics(OutputDeclarationSyntax syntax) { var assignedType = typeManager.GetTypeInfo(syntax); var valueType = typeManager.GetTypeInfo(syntax.Value); // this type is not a property in a symbol so the semantic error visitor won't collect the errors automatically if (valueType is ErrorTypeSymbol) { return(valueType.GetDiagnostics()); } if (TypeValidator.AreTypesAssignable(valueType, assignedType) == false) { return(DiagnosticBuilder.ForPosition(syntax.Value).OutputTypeMismatch(assignedType.Name, valueType.Name).AsEnumerable()); } return(Enumerable.Empty <Diagnostic>()); }
private DeclaredTypeAssignment?GetArrayAccessType(ArrayAccessSyntax syntax) { var baseExpressionAssignment = GetDeclaredTypeAssignment(syntax.BaseExpression); var indexAssignedType = this.typeManager.GetTypeInfo(syntax.IndexExpression); // TODO: Currently array access is broken with discriminated object types - revisit when that is fixed switch (baseExpressionAssignment?.Reference.Type) { case ArrayType arrayType when TypeValidator.AreTypesAssignable(indexAssignedType, LanguageConstants.Int): // we are accessing an array by an expression of a numeric type // return the item type of the array // for regular array we can't evaluate the array index at this point, but for loops the index is irrelevant // and we need to set declaring syntax, so property access can provide completions correctly for resource and module loops var declaringSyntax = baseExpressionAssignment.DeclaringSyntax is ForSyntax { Body : ObjectSyntax loopBody } ? loopBody : null; return(new DeclaredTypeAssignment(arrayType.Item.Type, declaringSyntax));
private TypeSymbol GetArrayAccessType(TypeManagerContext context, ArrayAccessSyntax syntax) { var errors = new List <ErrorDiagnostic>(); var baseType = this.GetTypeInfoInternal(context, syntax.BaseExpression); CollectErrors(errors, baseType); var indexType = this.GetTypeInfoInternal(context, syntax.IndexExpression); CollectErrors(errors, indexType); if (errors.Any() || indexType.TypeKind == TypeKind.Error) { return(new ErrorTypeSymbol(errors)); } if (baseType.TypeKind == TypeKind.Any) { // base expression is of type any if (indexType.TypeKind == TypeKind.Any) { // index is also of type any return(LanguageConstants.Any); } if (TypeValidator.AreTypesAssignable(indexType, LanguageConstants.Int) == true || TypeValidator.AreTypesAssignable(indexType, LanguageConstants.String) == true) { // index expression is string | int but base is any return(LanguageConstants.Any); } // index was of the wrong type return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.IndexExpression).StringOrIntegerIndexerRequired(indexType))); } if (baseType is ArrayType baseArray) { // we are indexing over an array if (TypeValidator.AreTypesAssignable(indexType, LanguageConstants.Int) == true) { // the index is of "any" type or integer type // return the item type return(baseArray.ItemType); } return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.IndexExpression).ArraysRequireIntegerIndex(indexType))); } if (baseType is ObjectType baseObject) { // we are indexing over an object if (indexType.TypeKind == TypeKind.Any) { // index is of type "any" return(GetExpressionedPropertyType(baseObject, syntax.IndexExpression)); } if (TypeValidator.AreTypesAssignable(indexType, LanguageConstants.String) == true) { switch (syntax.IndexExpression) { case StringSyntax @string when @string.IsInterpolated() == false: var propertyName = @string.GetLiteralValue(); return(this.GetNamedPropertyType(baseObject, syntax.IndexExpression, propertyName)); default: // the property name is itself an expression return(this.GetExpressionedPropertyType(baseObject, syntax.IndexExpression)); } } return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.IndexExpression).ObjectsRequireStringIndex(indexType))); } // index was of the wrong type return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.BaseExpression).IndexerRequiresObjectOrArray(baseType))); }
public static BinaryOperatorInfo?TryMatchExact(BinaryOperator @operator, TypeSymbol operandType1, TypeSymbol operandType2) { return(OperatorLookup[@operator] .SingleOrDefault(info => TypeValidator.AreTypesAssignable(operandType1, info.OperandType) == true && TypeValidator.AreTypesAssignable(operandType2, info.OperandType) == true)); }