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); }
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); }
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)); }
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)); }
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."); }
/// <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())); }