Exemplo n.º 1
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));
        }
Exemplo n.º 2
0
        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);
        }
Exemplo n.º 3
0
        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);
        }
Exemplo n.º 4
0
        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));
        }
Exemplo n.º 5
0
 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));
 }
Exemplo n.º 6
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));
        });
Exemplo n.º 7
0
        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>());
        }
Exemplo n.º 8
0
        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>());
        }
Exemplo n.º 9
0
        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));
Exemplo n.º 10
0
        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)));
        }
Exemplo n.º 11
0
 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));
 }