Example #1
0
        private DeclaredTypeAssignment?GetObjectType(ObjectSyntax syntax)
        {
            var parent = this.binder.GetParent(syntax);

            if (parent == null)
            {
                return(null);
            }

            var parentTypeAssignment = GetDeclaredTypeAssignment(parent);

            if (parentTypeAssignment == null)
            {
                return(null);
            }

            var parentType = parentTypeAssignment.Reference.Type;

            switch (parent)
            {
            case ResourceDeclarationSyntax _ when parentType is ResourceType resourceType:
                // the object literal's parent is a resource declaration, which makes this the body of the resource
                // the declared type will be the same as the parent
                return(TryCreateAssignment(ResolveDiscriminatedObjects(resourceType.Body.Type, syntax), syntax));

            case ModuleDeclarationSyntax _ when parentType is ModuleType moduleType:
                // the object literal's parent is a module declaration, which makes this the body of the module
                // the declared type will be the same as the parent
                return(TryCreateAssignment(ResolveDiscriminatedObjects(moduleType.Body.Type, syntax), syntax));

            case IfConditionSyntax _:
                // the if-condition already resolved the discriminated object, so we can reuse the type
                // but swap out the syntax for our object syntax
                return(TryCreateAssignment(parentType, syntax));

            case ParameterDeclarationSyntax parameterDeclaration when ReferenceEquals(parameterDeclaration.Modifier, syntax):
                // the object is a modifier of a parameter type
                // the declared type should be the appropriate modifier type
                // however we need the parameter's assigned type to determine the modifier type
                var parameterAssignedType = parameterDeclaration.GetAssignedType(this.typeManager);

                return(TryCreateAssignment(LanguageConstants.CreateParameterModifierType(parentType, parameterAssignedType), syntax));

            case ObjectPropertySyntax _:
                // the object is the value of a property of another object
                // use the declared type of the property and propagate the flags
                return(TryCreateAssignment(ResolveDiscriminatedObjects(parentType, syntax), syntax, parentTypeAssignment.Flags));

            case ArrayItemSyntax _:
                // the object is an item in an array
                // use the item's type and propagate flags
                return(TryCreateAssignment(ResolveDiscriminatedObjects(parentType, syntax), syntax, parentTypeAssignment.Flags));
            }

            return(null);
        }
Example #2
0
        private DeclaredTypeAssignment?GetObjectType(ObjectSyntax syntax)
        {
            // local function
            DeclaredTypeAssignment?CreateAssignment(ITypeReference?typeRef, DeclaredTypeFlags flags = DeclaredTypeFlags.None) => typeRef == null
                ? null
                : new DeclaredTypeAssignment(typeRef, syntax, flags);

            var parent = this.hierarchy.GetParent(syntax);

            if (parent == null)
            {
                return(null);
            }

            var parentTypeAssignment = GetDeclaredTypeAssignment(parent);

            if (parentTypeAssignment == null)
            {
                return(null);
            }

            var parentType = parentTypeAssignment.Reference.Type;

            switch (parent)
            {
            case ResourceDeclarationSyntax _ when parentType is ResourceType resourceType:
                // the object literal's parent is a resource declaration, which makes this the body of the resource
                // the declared type will be the same as the parent
                return(CreateAssignment(ResolveDiscriminatedObjects(resourceType.Body.Type, syntax)));

            case ModuleDeclarationSyntax _ when parentType is ModuleType moduleType:
                // the object literal's parent is a module declaration, which makes this the body of the module
                // the declared type will be the same as the parent
                return(CreateAssignment(ResolveDiscriminatedObjects(moduleType.Body.Type, syntax)));

            case ParameterDeclarationSyntax parameterDeclaration when ReferenceEquals(parameterDeclaration.Modifier, syntax):
                // the object is a modifier of a parameter type
                // the declared type should be the appropriate modifier type
                // however we need the parameter's assigned type to determine the modifier type
                var parameterAssignedType = parameterDeclaration.GetAssignedType(this.typeManager);

                return(CreateAssignment(LanguageConstants.CreateParameterModifierType(parentType, parameterAssignedType)));

            case ObjectPropertySyntax _:
                // the object is the value of a property of another object
                // use the declared type of the property and propagate the flags
                return(CreateAssignment(ResolveDiscriminatedObjects(parentType, syntax), parentTypeAssignment.Flags));

            case ArrayItemSyntax _:
                // the object is an item in an array
                // use the item's type and propagate flags
                return(CreateAssignment(ResolveDiscriminatedObjects(parentType, syntax), parentTypeAssignment.Flags));
            }

            return(null);
        }
Example #3
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 #4
0
        public override void VisitParameterDeclarationSyntax(ParameterDeclarationSyntax syntax)
        {
            diagnostics.AddRange(this.ValidateIdentifierAccess(syntax));

            var assignedType = typeManager.GetTypeInfo(syntax);

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

            case ObjectSyntax modifierSyntax:
                if (assignedType.TypeKind != TypeKind.Error && SyntaxHelper.TryGetPrimitiveType(syntax) is PrimitiveType primitiveType)
                {
                    var modifierType = LanguageConstants.CreateParameterModifierType(primitiveType, assignedType);
                    diagnostics.AddRange(TypeValidator.GetExpressionAssignmentDiagnostics(typeManager, modifierSyntax, modifierType));
                }
                break;
            }
        }
Example #5
0
        public override IEnumerable <ErrorDiagnostic> GetDiagnostics()
        {
            var diagnostics = this.ValidateIdentifierAccess();

            switch (this.Modifier)
            {
            case ParameterDefaultValueSyntax defaultValueSyntax:
                diagnostics = diagnostics.Concat(ValidateDefaultValue(defaultValueSyntax));
                break;

            case ObjectSyntax modifierSyntax:
                if (this.Type.TypeKind != TypeKind.Error && this.TryGetPrimitiveType() is TypeSymbol primitiveType)
                {
                    var modifierType = LanguageConstants.CreateParameterModifierType(primitiveType, this.Type);
                    diagnostics = diagnostics.Concat(TypeValidator.GetExpressionAssignmentDiagnostics(this.Context.TypeManager, modifierSyntax, modifierType));
                }
                break;
            }

            return(diagnostics);
        }
Example #6
0
        public void CompletelyInvalidArrayParameterModifier_ShouldLogExpectedErrors()
        {
            var obj = TestSyntaxFactory.CreateObject(new[]
            {
                // not a bool
                TestSyntaxFactory.CreateProperty("secure", TestSyntaxFactory.CreateInt(1)),

                // default value of wrong type
                TestSyntaxFactory.CreateProperty("default", TestSyntaxFactory.CreateBool(true)),

                // not an array
                TestSyntaxFactory.CreateProperty("allowed", TestSyntaxFactory.CreateObject(new ObjectPropertySyntax[0])),

                // not ints
                TestSyntaxFactory.CreateProperty("minValue", TestSyntaxFactory.CreateBool(true)),
                TestSyntaxFactory.CreateProperty("maxValue", TestSyntaxFactory.CreateString("11")),
                TestSyntaxFactory.CreateProperty("minLength", TestSyntaxFactory.CreateObject(new ObjectPropertySyntax[0])),
                TestSyntaxFactory.CreateProperty("maxLength", TestSyntaxFactory.CreateBool(false)),

                // extra property
                TestSyntaxFactory.CreateProperty("extra", TestSyntaxFactory.CreateBool(false)),

                TestSyntaxFactory.CreateProperty("metadata", TestSyntaxFactory.CreateObject(new[]
                {
                    // wrong type of description
                    TestSyntaxFactory.CreateProperty("description", TestSyntaxFactory.CreateInt(155))
                }))
            });

            TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, LanguageConstants.CreateParameterModifierType(LanguageConstants.Array))
            .Select(d => d.Message)
            .Should()
            .BeEquivalentTo(
                "The property 'default' expected a value of type array but the provided value is of type bool.",
                "The property 'maxLength' expected a value of type int but the provided value is of type bool.",
                "The property 'allowed' expected a value of type array[] but the provided value is of type object.",
                "The property 'minLength' expected a value of type int but the provided value is of type object.",
                "The property 'description' expected a value of type string but the provided value is of type int.",
                "The property 'secure' is not allowed on objects of type ParameterModifier_array.",
                "The property 'minValue' is not allowed on objects of type ParameterModifier_array.",
                "The property 'maxValue' is not allowed on objects of type ParameterModifier_array.",
                "The property 'extra' is not allowed on objects of type ParameterModifier_array.");
        }
Example #7
0
        public void ValidArrayParameterModifierShouldProduceNoDiagnostics()
        {
            var obj = TestSyntaxFactory.CreateObject(new[]
            {
                TestSyntaxFactory.CreateProperty("default", TestSyntaxFactory.CreateArray(new []
                {
                    TestSyntaxFactory.CreateArrayItem(TestSyntaxFactory.CreateBool(true))
                })),

                TestSyntaxFactory.CreateProperty("allowed", TestSyntaxFactory.CreateArray(new []
                {
                    TestSyntaxFactory.CreateArrayItem(TestSyntaxFactory.CreateArray(Enumerable.Empty <ArrayItemSyntax>()))
                })),

                TestSyntaxFactory.CreateProperty("minLength", TestSyntaxFactory.CreateInt(33)),
                TestSyntaxFactory.CreateProperty("maxLength", TestSyntaxFactory.CreateInt(25)),

                TestSyntaxFactory.CreateProperty("metadata", TestSyntaxFactory.CreateObject(new[]
                {
                    TestSyntaxFactory.CreateProperty("description", TestSyntaxFactory.CreateString("my description")),
                    TestSyntaxFactory.CreateProperty("extra1", TestSyntaxFactory.CreateString("extra")),
                    TestSyntaxFactory.CreateProperty("extra2", TestSyntaxFactory.CreateBool(true)),
                    TestSyntaxFactory.CreateProperty("extra3", TestSyntaxFactory.CreateInt(100))
                }))
            });

            TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, LanguageConstants.CreateParameterModifierType(LanguageConstants.Array)).Should().BeEmpty();
        }
Example #8
0
        public void ParameterModifierShouldRejectAdditionalProperties()
        {
            var obj = TestSyntaxFactory.CreateObject(new []
            {
                TestSyntaxFactory.CreateProperty("extra", TestSyntaxFactory.CreateString("foo")),
                TestSyntaxFactory.CreateProperty("extra2", TestSyntaxFactory.CreateString("foo"))
            });

            TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, LanguageConstants.CreateParameterModifierType(LanguageConstants.String))
            .Select(e => e.Message)
            .Should()
            .Equal(
                "The property 'extra' is not allowed on objects of type ParameterModifier_string.",
                "The property 'extra2' is not allowed on objects of type ParameterModifier_string.");
        }
Example #9
0
        public void EmptyModifierIsValid()
        {
            var obj = TestSyntaxFactory.CreateObject(new ObjectPropertySyntax[0]);

            TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, LanguageConstants.CreateParameterModifierType(LanguageConstants.Int)).Should().BeEmpty();
        }
        public void Valid_parameter_modifier_should_ensure_default_value_is_assignable_to_allowed_values()
        {
            var obj = TestSyntaxFactory.CreateObject(new[]
            {
                TestSyntaxFactory.CreateProperty("default", TestSyntaxFactory.CreateString("Three")),

                TestSyntaxFactory.CreateProperty("allowed", TestSyntaxFactory.CreateArray(new []
                {
                    TestSyntaxFactory.CreateArrayItem(TestSyntaxFactory.CreateString("One")),
                    TestSyntaxFactory.CreateArrayItem(TestSyntaxFactory.CreateString("Two")),
                })),
            });

            var allowedValuesType = UnionType.Create(new StringLiteralType("One"), new StringLiteralType("Two"));

            TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, LanguageConstants.CreateParameterModifierType(LanguageConstants.String, allowedValuesType))
            .Should().SatisfyRespectively(
                x => x.Message.Should().Be("The property 'default' expected a value of type 'One' | 'Two' but the provided value is of type 'Three'."));
        }
        public void ValidStringParameterModifierShouldProduceNoDiagnostics()
        {
            var obj = TestSyntaxFactory.CreateObject(new[]
            {
                TestSyntaxFactory.CreateProperty("secure", TestSyntaxFactory.CreateBool(false)),
                TestSyntaxFactory.CreateProperty("default", TestSyntaxFactory.CreateString("One")),

                TestSyntaxFactory.CreateProperty("allowed", TestSyntaxFactory.CreateArray(new []
                {
                    TestSyntaxFactory.CreateArrayItem(TestSyntaxFactory.CreateString("One")),
                    TestSyntaxFactory.CreateArrayItem(TestSyntaxFactory.CreateString("Two")),
                })),

                TestSyntaxFactory.CreateProperty("minLength", TestSyntaxFactory.CreateInt(33)),
                TestSyntaxFactory.CreateProperty("maxLength", TestSyntaxFactory.CreateInt(25)),

                TestSyntaxFactory.CreateProperty("metadata", TestSyntaxFactory.CreateObject(new[]
                {
                    TestSyntaxFactory.CreateProperty("description", TestSyntaxFactory.CreateString("my description")),
                    TestSyntaxFactory.CreateProperty("extra1", TestSyntaxFactory.CreateString("extra")),
                    TestSyntaxFactory.CreateProperty("extra2", TestSyntaxFactory.CreateBool(true)),
                    TestSyntaxFactory.CreateProperty("extra3", TestSyntaxFactory.CreateInt(100))
                }))
            });

            var allowedValuesType = UnionType.Create(new StringLiteralType("One"), new StringLiteralType("Two"));

            TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, LanguageConstants.CreateParameterModifierType(LanguageConstants.String, allowedValuesType)).Should().BeEmpty();
        }
        public void ParameterModifierShouldRejectAdditionalProperties()
        {
            var obj = TestSyntaxFactory.CreateObject(new []
            {
                TestSyntaxFactory.CreateProperty("extra", TestSyntaxFactory.CreateString("foo")),
                TestSyntaxFactory.CreateProperty("extra2", TestSyntaxFactory.CreateString("foo"))
            });

            TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, LanguageConstants.CreateParameterModifierType(LanguageConstants.String, LanguageConstants.String))
            .Select(e => e.Message)
            .Should()
            .Equal(
                "The property 'extra' is not allowed on objects of type ParameterModifier<string>. Permissible properties include 'allowed', 'default', 'maxLength', 'metadata', 'minLength', 'secure'.",
                "The property 'extra2' is not allowed on objects of type ParameterModifier<string>. Permissible properties include 'allowed', 'default', 'maxLength', 'metadata', 'minLength', 'secure'.");
        }
        public void CompletelyInvalidBoolParameterModifier_ShouldLogExpectedErrors()
        {
            var obj = TestSyntaxFactory.CreateObject(new[]
            {
                // not a bool and not allowed
                TestSyntaxFactory.CreateProperty("secure", TestSyntaxFactory.CreateInt(1)),

                // default value of wrong type
                TestSyntaxFactory.CreateProperty("default", TestSyntaxFactory.CreateInt(1231)),

                // not an array
                TestSyntaxFactory.CreateProperty("allowed", TestSyntaxFactory.CreateArray(new []
                {
                    TestSyntaxFactory.CreateArrayItem(TestSyntaxFactory.CreateInt(22))
                })),

                // not allowed
                TestSyntaxFactory.CreateProperty("minValue", TestSyntaxFactory.CreateBool(true)),
                TestSyntaxFactory.CreateProperty("maxValue", TestSyntaxFactory.CreateString("11")),
                TestSyntaxFactory.CreateProperty("minLength", TestSyntaxFactory.CreateObject(new ObjectPropertySyntax[0])),
                TestSyntaxFactory.CreateProperty("maxLength", TestSyntaxFactory.CreateBool(false)),

                // extra property
                TestSyntaxFactory.CreateProperty("extra", TestSyntaxFactory.CreateBool(false)),

                TestSyntaxFactory.CreateProperty("metadata", TestSyntaxFactory.CreateObject(new[]
                {
                    // wrong type of description
                    TestSyntaxFactory.CreateProperty("description", TestSyntaxFactory.CreateInt(155))
                }))
            });

            TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, LanguageConstants.CreateParameterModifierType(LanguageConstants.Bool))
            .Select(d => d.Message)
            .Should().BeEquivalentTo(
                "The property 'default' expected a value of type 'bool' but the provided value is of type 'int'.",
                "The enclosing array expected an item of type 'bool', but the provided item was of type 'int'.",
                "The property 'description' expected a value of type 'string' but the provided value is of type 'int'.",
                "The property 'secure' is not allowed on objects of type 'ParameterModifier_bool'.",
                "The property 'minValue' is not allowed on objects of type 'ParameterModifier_bool'.",
                "The property 'maxValue' is not allowed on objects of type 'ParameterModifier_bool'.",
                "The property 'minLength' is not allowed on objects of type 'ParameterModifier_bool'.",
                "The property 'maxLength' is not allowed on objects of type 'ParameterModifier_bool'.",
                "The property 'extra' is not allowed on objects of type 'ParameterModifier_bool'.");
        }