public TypeSymbol GetDeclaredType(ResourceScope targetScope, IResourceTypeProvider resourceTypeProvider)
        {
            var stringSyntax = this.TypeString;

            if (stringSyntax != null && stringSyntax.IsInterpolated())
            {
                // TODO: in the future, we can relax this check to allow interpolation with compile-time constants.
                // right now, codegen will still generate a format string however, which will cause problems for the type.
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).ResourceTypeInterpolationUnsupported()));
            }

            var stringContent = stringSyntax?.TryGetLiteralValue();

            if (stringContent == null)
            {
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidResourceType()));
            }

            var typeReference = ResourceTypeReference.TryParse(stringContent);

            if (typeReference == null)
            {
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidResourceType()));
            }

            return(resourceTypeProvider.GetType(targetScope, typeReference));
        }
        public TypeSymbol GetAssignedType(ITypeManager typeManager)
        {
            var assignedType = this.GetDeclaredType();

            var allowedSyntax = SyntaxHelper.TryGetAllowedSyntax(this);

            if (allowedSyntax != null && !allowedSyntax.Items.Any())
            {
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(allowedSyntax).AllowedMustContainItems()));
            }

            if (object.ReferenceEquals(assignedType, LanguageConstants.String))
            {
                var allowedItemTypes = allowedSyntax?.Items.Select(typeManager.GetTypeInfo);

                if (allowedItemTypes != null && allowedItemTypes.All(itemType => itemType is StringLiteralType))
                {
                    assignedType = UnionType.Create(allowedItemTypes);
                }
                else
                {
                    // In order to support assignment for a generic string to enum-typed properties (which generally is forbidden),
                    // we need to relax the validation for string parameters without 'allowed' values specified.
                    assignedType = LanguageConstants.LooseString;
                }
            }

            return(assignedType);
        }
Esempio n. 3
0
        public TypeSymbol GetAssignedType(ITypeManager typeManager, ArraySyntax?allowedSyntax)
        {
            var assignedType = this.GetDeclaredType();

            // TODO: remove SyntaxHelper.TryGetAllowedSyntax when we drop parameter modifiers support.
            if (allowedSyntax is not null && !allowedSyntax.Items.Any())
            {
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(allowedSyntax).AllowedMustContainItems()));
            }

            var allowedItemTypes = allowedSyntax?.Items.Select(typeManager.GetTypeInfo);

            if (ReferenceEquals(assignedType, LanguageConstants.String))
            {
                if (allowedItemTypes?.All(itemType => itemType is StringLiteralType) == true)
                {
                    assignedType = UnionType.Create(allowedItemTypes);
                }
                else
                {
                    // In order to support assignment for a generic string to enum-typed properties (which generally is forbidden),
                    // we need to relax the validation for string parameters without 'allowed' values specified.
                    assignedType = LanguageConstants.LooseString;
                }
            }

            if (ReferenceEquals(assignedType, LanguageConstants.Array) &&
                allowedItemTypes?.All(itemType => itemType is StringLiteralType) == true)
            {
                assignedType = new TypedArrayType(UnionType.Create(allowedItemTypes), TypeSymbolValidationFlags.Default);
            }

            return(assignedType);
        }
Esempio n. 4
0
        public TypeSymbol GetDeclaredType()
        {
            // assume "any" type when the parameter has parse errors (either missing or was skipped)
            var declaredType = this.ParameterType == null
                ? LanguageConstants.Any
                : LanguageConstants.TryGetDeclarationType(this.ParameterType.TypeName);

            if (declaredType == null)
            {
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidParameterType()));
            }

            return(declaredType);
        }
        public TypeSymbol GetDeclaredType(IBinder binder)
        {
            if (binder.GetSymbolInfo(this) is not ModuleSymbol moduleSymbol)
            {
                // TODO: Ideally we'd still be able to return a type here, but we'd need access to the compilation to get it.
                return(ErrorType.Empty());
            }

            if (!moduleSymbol.TryGetSemanticModel(out var moduleSemanticModel, out var failureDiagnostic))
            {
                return(ErrorType.Create(failureDiagnostic));
            }

            return(LanguageConstants.CreateModuleType(
                       moduleSemanticModel.ParameterTypeProperties,
                       moduleSemanticModel.OutputTypeProperties,
                       moduleSemanticModel.TargetScope,
                       binder.TargetScope,
                       LanguageConstants.TypeNameModule));
        }
Esempio n. 6
0
        private static object CreateMockParameter(ParameterInfo parameter, int index)
        {
            if (parameter.ParameterType == typeof(TypeSymbol))
            {
                return(new PrimitiveType($"<type_{index}>", TypeSymbolValidationFlags.Default));
            }

            if (parameter.ParameterType == typeof(IList <TypeSymbol>))
            {
                return(new List <TypeSymbol> {
                    new PrimitiveType($"<list_type_{index}>", TypeSymbolValidationFlags.Default)
                });
            }

            if (parameter.ParameterType == typeof(IEnumerable <string>))
            {
                return(new List <string> {
                    $"<value_{index}"
                });
            }

            if (parameter.ParameterType == typeof(IList <string>))
            {
                return(new List <string> {
                    $"<value_{index}"
                });
            }

            if (parameter.ParameterType == typeof(Uri))
            {
                return(new Uri("file:///path/to/main.bicep"));
            }

            if (parameter.ParameterType == typeof(Symbol))
            {
                // just using this one as it's easy to construct
                return(ErrorType.Create(Enumerable.Empty <ErrorDiagnostic>()));
            }

            if (parameter.ParameterType == typeof(int) || parameter.ParameterType == typeof(int?))
            {
                return(0);
            }

            if (parameter.ParameterType == typeof(bool) || parameter.ParameterType == typeof(bool?))
            {
                return(false);
            }

            if (parameter.ParameterType == typeof(SymbolKind))
            {
                return(SymbolKind.Variable);
            }

            if (parameter.ParameterType == typeof(ResourceTypeReference))
            {
                return(ResourceTypeReference.Parse("Mock.ErrorParam/mockResources@2020-01-01"));
            }

            return($"<param_{index}>");
        }
        /// <summary>
        /// Returns the declared type of the resource body (based on the type string).
        /// Returns the same value for single resource or resource loops declarations.
        /// </summary>
        /// <param name="resourceTypeProvider">resource type provider</param>
        public TypeSymbol GetDeclaredType(IBinder binder, IResourceTypeProvider resourceTypeProvider)
        {
            var stringSyntax = this.TypeString;

            if (stringSyntax != null && stringSyntax.IsInterpolated())
            {
                // TODO: in the future, we can relax this check to allow interpolation with compile-time constants.
                // right now, codegen will still generate a format string however, which will cause problems for the type.
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).ResourceTypeInterpolationUnsupported()));
            }

            var stringContent = stringSyntax?.TryGetLiteralValue();

            if (stringContent == null)
            {
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidResourceType()));
            }

            // Before we parse the type name we need to determine if it's a top level resource or not.
            ResourceTypeReference?typeReference;
            var  hasParentDeclaration          = false;
            var  nestedParents                 = binder.GetAllAncestors <ResourceDeclarationSyntax>(this);
            bool isTopLevelResourceDeclaration = nestedParents.Length == 0;

            if (isTopLevelResourceDeclaration)
            {
                // This is a top level resource - the type is a fully-qualified type.
                typeReference = ResourceTypeReference.TryParse(stringContent);
                if (typeReference == null)
                {
                    return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidResourceType()));
                }

                if (binder.GetSymbolInfo(this) is ResourceSymbol resourceSymbol &&
                    binder.TryGetCycle(resourceSymbol) is null &&
                    resourceSymbol.SafeGetBodyPropertyValue(LanguageConstants.ResourceParentPropertyName) is {} referenceParentSyntax&&
                    binder.GetSymbolInfo(referenceParentSyntax) is ResourceSymbol parentResourceSymbol)
                {
                    hasParentDeclaration = true;

                    var parentType = parentResourceSymbol.DeclaringResource.GetDeclaredType(binder, resourceTypeProvider);
                    if (parentType is not ResourceType parentResourceType)
                    {
                        // TODO should we raise an error, or just rely on the error on the parent?
                        return(ErrorType.Create(DiagnosticBuilder.ForPosition(referenceParentSyntax).ParentResourceTypeHasErrors(parentResourceSymbol.DeclaringResource.Name.IdentifierName)));
                    }

                    if (!parentResourceType.TypeReference.IsParentOf(typeReference))
                    {
                        return(ErrorType.Create(DiagnosticBuilder.ForPosition(referenceParentSyntax).ResourceTypeIsNotValidParent(
                                                    typeReference.FullyQualifiedType,
                                                    parentResourceType.TypeReference.FullyQualifiedType)));
                    }
                }
            }
            else
            {
                // This is a nested resource, the type name is a compound of all of the ancestor
                // type names.
                //
                // Ex: 'My.Rp/someType@2020-01-01' -> 'someChild' -> 'someGrandchild'

                // The top-most resource must have a qualified type name.
                hasParentDeclaration = true;
                var baseTypeStringContent = nestedParents[0].TypeString?.TryGetLiteralValue();
                if (baseTypeStringContent == null)
                {
                    return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidAncestorResourceType(nestedParents[0].Name.IdentifierName)));
                }

                var baseTypeReference = ResourceTypeReference.TryParse(baseTypeStringContent);
                if (baseTypeReference == null)
                {
                    return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidAncestorResourceType(nestedParents[0].Name.IdentifierName)));
                }

                // Collect each other ancestor resource's type.
                var typeSegments = new List <string>();
                for (var i = 1; i < nestedParents.Length; i++)
                {
                    var typeSegmentStringContent = nestedParents[i].TypeString?.TryGetLiteralValue();
                    if (typeSegmentStringContent == null)
                    {
                        return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidAncestorResourceType(nestedParents[i].Name.IdentifierName)));
                    }

                    typeSegments.Add(typeSegmentStringContent);
                }

                // Add *this* resource's type
                typeSegments.Add(stringContent);

                // If this fails, let's walk through and find the root cause. This could be confusing
                // for people seeing it the first time.
                typeReference = ResourceTypeReference.TryCombine(baseTypeReference, typeSegments);
                if (typeReference == null)
                {
                    // We'll special case the last one since it refers to *this* resource. We don't
                    // want to cascade a bunch of noisy errors for parents, they get their own errors.
                    for (var j = 0; j < typeSegments.Count - 1; j++)
                    {
                        if (!ResourceTypeReference.TryParseSingleTypeSegment(typeSegments[j], out _, out _))
                        {
                            return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidAncestorResourceType(nestedParents[j + 1].Name.IdentifierName)));
                        }
                    }

                    if (!ResourceTypeReference.TryParseSingleTypeSegment(stringContent, out _, out _))
                    {
                        // OK this resource is the one that's wrong.
                        return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidResourceTypeSegment(stringContent)));
                    }

                    // Something went wrong, this should be unreachable.
                    throw new InvalidOperationException("Failed to find the root cause of an invalid compound resource type.");
                }
            }

            var flags = ResourceTypeGenerationFlags.None;

            if (IsExistingResource())
            {
                flags |= ResourceTypeGenerationFlags.ExistingResource;
            }

            if (!isTopLevelResourceDeclaration)
            {
                flags |= ResourceTypeGenerationFlags.NestedResource;
            }

            if (typeReference.IsRootType || hasParentDeclaration)
            {
                flags |= ResourceTypeGenerationFlags.PermitLiteralNameProperty;
            }

            return(resourceTypeProvider.GetType(typeReference, flags));
        }
Esempio n. 8
0
        private static object CreateMockParameter(ParameterInfo parameter, int index)
        {
            if (parameter.ParameterType == typeof(TypeSymbol))
            {
                return(new PrimitiveType($"<type_{index}>", TypeSymbolValidationFlags.Default));
            }

            if (parameter.ParameterType == typeof(IList <TypeSymbol>))
            {
                return(new List <TypeSymbol> {
                    new PrimitiveType($"<list_type_{index}>", TypeSymbolValidationFlags.Default)
                });
            }

            if (parameter.ParameterType == typeof(IEnumerable <string>))
            {
                return(new List <string> {
                    $"<value_{index}"
                });
            }

            if (parameter.ParameterType == typeof(IList <string>))
            {
                return(new List <string> {
                    $"<value_{index}"
                });
            }

            if (parameter.ParameterType == typeof(ImmutableArray <string>))
            {
                return(new[] { $"<value_{index}" }.ToImmutableArray());
            }

            if (parameter.ParameterType == typeof(Uri))
            {
                return(new Uri("file:///path/to/main.bicep"));
            }

            if (parameter.ParameterType == typeof(Symbol))
            {
                // just using this one as it's easy to construct
                return(ErrorType.Create(Enumerable.Empty <ErrorDiagnostic>()));
            }

            if (parameter.ParameterType == typeof(int) || parameter.ParameterType == typeof(int?))
            {
                return(0);
            }

            if (parameter.ParameterType == typeof(long) || parameter.ParameterType == typeof(long?))
            {
                return(0);
            }

            if (parameter.ParameterType == typeof(bool) || parameter.ParameterType == typeof(bool?))
            {
                return(false);
            }

            if (parameter.ParameterType == typeof(SymbolKind))
            {
                return(SymbolKind.Variable);
            }

            if (parameter.ParameterType == typeof(ResourceTypeReference))
            {
                return(ResourceTypeReference.Parse("Mock.ErrorParam/mockResources@2020-01-01"));
            }

            if (parameter.ParameterType == typeof(ResourceScope))
            {
                return(ResourceScope.ResourceGroup);
            }

            if (parameter.ParameterType == typeof(string) || parameter.ParameterType == typeof(IEnumerable <char>))
            {
                return($"<param_{index}>");
            }

            if (parameter.ParameterType == typeof(ObjectSyntax))
            {
                return(TestSyntaxFactory.CreateObject(Array.Empty <ObjectPropertySyntax>()));
            }

            throw new AssertFailedException($"Unable to generate mock parameter value of type '{parameter.ParameterType}' for the diagnostic builder method.");
        }
Esempio n. 9
0
        /// <summary>
        /// Returns the declared type of the resource body (based on the type string).
        /// Returns the same value for single resource or resource loops declarations.
        /// </summary>
        /// <param name="resourceTypeProvider">resource type provider</param>
        public TypeSymbol GetDeclaredType(IBinder binder, IResourceTypeProvider resourceTypeProvider)
        {
            var stringSyntax = this.TypeString;

            if (stringSyntax != null && stringSyntax.IsInterpolated())
            {
                // TODO: in the future, we can relax this check to allow interpolation with compile-time constants.
                // right now, codegen will still generate a format string however, which will cause problems for the type.
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).ResourceTypeInterpolationUnsupported()));
            }

            var stringContent = stringSyntax?.TryGetLiteralValue();

            if (stringContent == null)
            {
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidResourceType()));
            }

            // Before we parse the type name we need to determine if it's a top level resource or not.
            ResourceTypeReference?typeReference;
            var ancestors = binder.GetAllAncestors <ResourceDeclarationSyntax>(this);

            if (ancestors.Length == 0)
            {
                // This is a top level resource - the type is a fully-qualified type.
                typeReference = ResourceTypeReference.TryParse(stringContent);
                if (typeReference == null)
                {
                    return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidResourceType()));
                }
            }
            else
            {
                // This is a nested resource, the type name is a compound of all of the ancestor
                // type names.
                //
                // Ex: 'My.Rp/someType@2020-01-01' -> 'someChild' -> 'someGrandchild'

                // The top-most resource must have a qualified type name.
                var baseTypeStringContent = ancestors[0].TypeString?.TryGetLiteralValue();
                if (baseTypeStringContent == null)
                {
                    return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidAncestorResourceType(ancestors[0].Name.IdentifierName)));
                }

                var baseTypeReference = ResourceTypeReference.TryParse(baseTypeStringContent);
                if (baseTypeReference == null)
                {
                    return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidAncestorResourceType(ancestors[0].Name.IdentifierName)));
                }

                // Collect each other ancestor resource's type.
                var typeSegments = new List <string>();
                for (var i = 1; i < ancestors.Length; i++)
                {
                    var typeSegmentStringContent = ancestors[i].TypeString?.TryGetLiteralValue();
                    if (typeSegmentStringContent == null)
                    {
                        return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidAncestorResourceType(ancestors[i].Name.IdentifierName)));
                    }

                    typeSegments.Add(typeSegmentStringContent);
                }

                // Add *this* resource's type
                typeSegments.Add(stringContent);

                // If this fails, let's walk through and find the root cause. This could be confusing
                // for people seeing it the first time.
                typeReference = ResourceTypeReference.TryCombine(baseTypeReference, typeSegments);
                if (typeReference == null)
                {
                    // We'll special case the last one since it refers to *this* resource. We don't
                    // want to cascade a bunch of noisy errors for parents, they get their own errors.
                    for (var j = 0; j < typeSegments.Count - 1; j++)
                    {
                        if (!ResourceTypeReference.TryParseSingleTypeSegment(typeSegments[j], out _, out _))
                        {
                            return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidAncestorResourceType(ancestors[j + 1].Name.IdentifierName)));
                        }
                    }

                    if (!ResourceTypeReference.TryParseSingleTypeSegment(stringContent, out _, out _))
                    {
                        // OK this resource is the one that's wrong.
                        return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidResourceTypeSegment(stringContent)));
                    }

                    // Something went wrong, this should be unreachable.
                    throw new InvalidOperationException("Failed to find the root cause of an invalid compound resource type.");
                }
            }

            return(resourceTypeProvider.GetType(typeReference, IsExistingResource()));
        }