diff --git a/src/Bicep.Core.IntegrationTests/ScopeTests.cs b/src/Bicep.Core.IntegrationTests/ScopeTests.cs index 5b3cf8c2e6c..2aa4e6b7084 100644 --- a/src/Bicep.Core.IntegrationTests/ScopeTests.cs +++ b/src/Bicep.Core.IntegrationTests/ScopeTests.cs @@ -248,7 +248,7 @@ public void Errors_are_raised_for_existing_resources_at_invalid_scopes() ")); diags.Should().HaveDiagnostics(new[] { - ("BCP124", DiagnosticLevel.Error, "The supplied scope \"subscription\" is not valid for this resource type. Permitted scopes: \"resourceGroup\"."), + ("BCP124", DiagnosticLevel.Error, "Scope \"subscription\" is not valid for this resource type. Permitted scopes: \"resourceGroup\"."), }); // use an invalid targetScope without setting the scope property @@ -260,11 +260,9 @@ public void Errors_are_raised_for_existing_resources_at_invalid_scopes() } ")); -/* TODO make this test pass diags.Should().HaveDiagnostics(new[] { - ("BCP124", DiagnosticLevel.Error, "The supplied scope \"subscription\" is not valid for this resource type. Permitted scopes: \"resourceGroup\"."), + ("BCP124", DiagnosticLevel.Error, "Scope \"subscription\" is not valid for this resource type. Permitted scopes: \"resourceGroup\"."), }); -*/ } } } \ No newline at end of file diff --git a/src/Bicep.Core.Samples/Files/InvalidModules_LF/main.diagnostics.bicep b/src/Bicep.Core.Samples/Files/InvalidModules_LF/main.diagnostics.bicep index ae45f87b2ec..1204e128eca 100644 --- a/src/Bicep.Core.Samples/Files/InvalidModules_LF/main.diagnostics.bicep +++ b/src/Bicep.Core.Samples/Files/InvalidModules_LF/main.diagnostics.bicep @@ -234,6 +234,7 @@ module moduleWithInvalidScope './empty.bicep' = { module moduleWithMissingRequiredScope './subscription_empty.bicep' = { //@[7:37) [BCP035 (Error)] The specified "module" declaration is missing the following required properties: "scope". |moduleWithMissingRequiredScope| +//@[69:113) [BCP123 (Error)] Scope "resourceGroup" is not valid for this module. Permitted scopes: "subscription". |{\n name: 'moduleWithMissingRequiredScope'\n}| name: 'moduleWithMissingRequiredScope' } diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/main.diagnostics.bicep b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/main.diagnostics.bicep index de596c26a2a..a9ca89a650d 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/main.diagnostics.bicep +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/main.diagnostics.bicep @@ -700,13 +700,11 @@ resource invalidScope 'My.Rp/mockResource@2020-12-01' = { resource invalidScope2 'My.Rp/mockResource@2020-12-01' = { name: 'invalidScope2' scope: resourceGroup() -//@[9:24) [BCP124 (Error)] The supplied scope "resourceGroup" is not valid for this resource type. Permitted scopes: "resource". |resourceGroup()| } resource invalidScope3 'My.Rp/mockResource@2020-12-01' = { name: 'invalidScope3' scope: subscription() -//@[9:23) [BCP124 (Error)] The supplied scope "subscription" is not valid for this resource type. Permitted scopes: "resource". |subscription()| } resource invalidDuplicateName1 'Mock.Rp/mockResource@2020-01-01' = { diff --git a/src/Bicep.Core.UnitTests/Utils/ResourceTypeProviderHelper.cs b/src/Bicep.Core.UnitTests/Utils/ResourceTypeProviderHelper.cs index 15ff297c82b..6382f4ec1a7 100644 --- a/src/Bicep.Core.UnitTests/Utils/ResourceTypeProviderHelper.cs +++ b/src/Bicep.Core.UnitTests/Utils/ResourceTypeProviderHelper.cs @@ -49,7 +49,8 @@ public static ResourceType CreateCustomResourceType(string fullyQualifiedType, s var resourceProperties = LanguageConstants.GetCommonResourceProperties(reference) .Concat(new TypeProperty("properties", new NamedObjectType("properties", validationFlags, customProperties, null), TypePropertyFlags.Required)); - return new ResourceType(reference, new NamedObjectType(reference.FormatName(), validationFlags, resourceProperties, null)); + var bodyType = new NamedObjectType(reference.FormatName(), validationFlags, resourceProperties, null); + return new ResourceType(reference, ResourceScope.Tenant | ResourceScope.ManagementGroup | ResourceScope.Subscription | ResourceScope.ResourceGroup | ResourceScope.Resource, bodyType); } public static AzResourceTypeProvider CreateAzResourceTypeProvider(Action typeFactoryFunc) diff --git a/src/Bicep.Core.UnitTests/Utils/TestResourceTypeProvider.cs b/src/Bicep.Core.UnitTests/Utils/TestResourceTypeProvider.cs index 82b02e3c180..883438a0583 100644 --- a/src/Bicep.Core.UnitTests/Utils/TestResourceTypeProvider.cs +++ b/src/Bicep.Core.UnitTests/Utils/TestResourceTypeProvider.cs @@ -14,9 +14,10 @@ public class TestResourceTypeProvider : IResourceTypeProvider { public ResourceType GetType(ResourceTypeReference reference, bool isExistingResource) { - var resourceType = new ResourceType(reference, new NamedObjectType(reference.FormatName(), TypeSymbolValidationFlags.Default, LanguageConstants.CreateResourceProperties(reference), null)); + var bodyType = new NamedObjectType(reference.FormatName(), TypeSymbolValidationFlags.Default, LanguageConstants.CreateResourceProperties(reference), null); + var resourceType = new ResourceType(reference, ResourceScope.Tenant | ResourceScope.ManagementGroup | ResourceScope.Subscription | ResourceScope.ResourceGroup | ResourceScope.Resource, bodyType); - return AzResourceTypeProvider.SetBicepResourceProperties(resourceType, Azure.Bicep.Types.Concrete.ScopeType.Unknown, isExistingResource); + return AzResourceTypeProvider.SetBicepResourceProperties(resourceType, isExistingResource); } public bool HasType(ResourceTypeReference typeReference) diff --git a/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs b/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs index eeae23fb8be..01fca1c7511 100644 --- a/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs +++ b/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs @@ -745,13 +745,13 @@ public Diagnostic RuntimePropertyNotAllowed(string property, IEnumerable TextSpan, DiagnosticLevel.Error, "BCP123", - $"The supplied scope {ToQuotedString(GetResourceScopeDescriptions(suppliedScope))} is not valid for this module. Permitted scopes: {ToQuotedString(GetResourceScopeDescriptions(supportedScopes))}."); + $"Scope {ToQuotedString(GetResourceScopeDescriptions(suppliedScope))} is not valid for this module. Permitted scopes: {ToQuotedString(GetResourceScopeDescriptions(supportedScopes))}."); public Diagnostic UnsupportedResourceScope(ResourceScope suppliedScope, ResourceScope supportedScopes) => new( TextSpan, DiagnosticLevel.Error, "BCP124", - $"The supplied scope {ToQuotedString(GetResourceScopeDescriptions(suppliedScope))} is not valid for this resource type. Permitted scopes: {ToQuotedString(GetResourceScopeDescriptions(supportedScopes))}."); + $"Scope {ToQuotedString(GetResourceScopeDescriptions(suppliedScope))} is not valid for this resource type. Permitted scopes: {ToQuotedString(GetResourceScopeDescriptions(supportedScopes))}."); } public static DiagnosticBuilderInternal ForPosition(TextSpan span) diff --git a/src/Bicep.Core/Emit/EmitLimitationCalculator.cs b/src/Bicep.Core/Emit/EmitLimitationCalculator.cs index 7d375473ca0..7a943676b49 100644 --- a/src/Bicep.Core/Emit/EmitLimitationCalculator.cs +++ b/src/Bicep.Core/Emit/EmitLimitationCalculator.cs @@ -37,8 +37,14 @@ void logInvalidScopeDiagnostic(IPositionable positionable, ResourceScope supplie foreach (var resourceSymbol in semanticModel.Root.ResourceDeclarations) { + if (resourceSymbol.Type is not ResourceType resourceType) + { + // missing type should be caught during type validation + continue; + } + var scopeProperty = resourceSymbol.SafeGetBodyProperty(LanguageConstants.ResourceScopePropertyName); - var scopeData = ScopeHelper.ValidateScope(semanticModel, logInvalidScopeDiagnostic, scopeProperty); + var scopeData = ScopeHelper.ValidateScope(semanticModel, logInvalidScopeDiagnostic, resourceType.ValidParentScopes, resourceSymbol.DeclaringResource.Body, scopeProperty); if (scopeData is null) { @@ -60,8 +66,14 @@ void logInvalidScopeDiagnostic(IPositionable positionable, ResourceScope supplie foreach (var moduleSymbol in semanticModel.Root.ModuleDeclarations) { + if (moduleSymbol.Type is not ModuleType moduleType) + { + // missing type should be caught during type validation + continue; + } + var scopeProperty = moduleSymbol.SafeGetBodyProperty(LanguageConstants.ResourceScopePropertyName); - var scopeData = ScopeHelper.ValidateScope(semanticModel, logInvalidScopeDiagnostic, scopeProperty); + var scopeData = ScopeHelper.ValidateScope(semanticModel, logInvalidScopeDiagnostic, moduleType.ValidParentScopes, moduleSymbol.DeclaringModule.Body, scopeProperty); if (scopeData is null) { diff --git a/src/Bicep.Core/Emit/ScopeHelper.cs b/src/Bicep.Core/Emit/ScopeHelper.cs index 34a81975f31..759e32fcc2c 100644 --- a/src/Bicep.Core/Emit/ScopeHelper.cs +++ b/src/Bicep.Core/Emit/ScopeHelper.cs @@ -30,11 +30,16 @@ public class ScopeData public delegate void LogInvalidScopeDiagnostic(IPositionable positionable, ResourceScope suppliedScope, ResourceScope supportedScopes); - public static ScopeData? ValidateScope(SemanticModel semanticModel, LogInvalidScopeDiagnostic logInvalidScopeFunc, ObjectPropertySyntax? scopeProperty) + public static ScopeData? ValidateScope(SemanticModel semanticModel, LogInvalidScopeDiagnostic logInvalidScopeFunc, ResourceScope supportedScopes, SyntaxBase bodySyntax, ObjectPropertySyntax? scopeProperty) { - if (scopeProperty is null || semanticModel.GetDeclaredType(scopeProperty) is not IScopeReference declaredScope) + if (scopeProperty is null) { // no scope provided - use the target scope for the file + if (!supportedScopes.HasFlag(semanticModel.TargetScope)) + { + logInvalidScopeFunc(bodySyntax, semanticModel.TargetScope, supportedScopes); + } + return null; } @@ -44,17 +49,17 @@ public class ScopeData switch (scopeType) { case TenantScopeType type: - if (!declaredScope.Scope.HasFlag(ResourceScope.Tenant)) + if (!supportedScopes.HasFlag(ResourceScope.Tenant)) { - logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Tenant, declaredScope.Scope); + logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Tenant, supportedScopes); return null; } return new ScopeData { RequestedScope = ResourceScope.Tenant }; case ManagementGroupScopeType type: - if (!declaredScope.Scope.HasFlag(ResourceScope.ManagementGroup)) + if (!supportedScopes.HasFlag(ResourceScope.ManagementGroup)) { - logInvalidScopeFunc(scopeProperty.Value, ResourceScope.ManagementGroup, declaredScope.Scope); + logInvalidScopeFunc(scopeProperty.Value, ResourceScope.ManagementGroup, supportedScopes); return null; } @@ -63,9 +68,9 @@ public class ScopeData _ => new ScopeData { RequestedScope = ResourceScope.ManagementGroup, ManagementGroupNameProperty = type.Arguments[0].Expression }, }; case SubscriptionScopeType type: - if (!declaredScope.Scope.HasFlag(ResourceScope.Subscription)) + if (!supportedScopes.HasFlag(ResourceScope.Subscription)) { - logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Subscription, declaredScope.Scope); + logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Subscription, supportedScopes); return null; } @@ -74,9 +79,9 @@ public class ScopeData _ => new ScopeData { RequestedScope = ResourceScope.Subscription, SubscriptionIdProperty = type.Arguments[0].Expression }, }; case ResourceGroupScopeType type: - if (!declaredScope.Scope.HasFlag(ResourceScope.ResourceGroup)) + if (!supportedScopes.HasFlag(ResourceScope.ResourceGroup)) { - logInvalidScopeFunc(scopeProperty.Value, ResourceScope.ResourceGroup, declaredScope.Scope); + logInvalidScopeFunc(scopeProperty.Value, ResourceScope.ResourceGroup, supportedScopes); return null; } @@ -86,9 +91,9 @@ public class ScopeData _ => new ScopeData { RequestedScope = ResourceScope.ResourceGroup, SubscriptionIdProperty = type.Arguments[0].Expression, ResourceGroupProperty = type.Arguments[1].Expression }, }; case {} when scopeSymbol is ResourceSymbol targetResourceSymbol: - if (!declaredScope.Scope.HasFlag(ResourceScope.Resource)) + if (!supportedScopes.HasFlag(ResourceScope.Resource)) { - logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Resource, declaredScope.Scope); + logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Resource, supportedScopes); return null; } diff --git a/src/Bicep.Core/LanguageConstants.cs b/src/Bicep.Core/LanguageConstants.cs index 621eacc4364..ae6c1ab1344 100644 --- a/src/Bicep.Core/LanguageConstants.cs +++ b/src/Bicep.Core/LanguageConstants.cs @@ -187,7 +187,7 @@ public static TypeSymbol CreateModuleType(IEnumerable paramsProper }, null); - return new ModuleType(typeName, moduleBody); + return new ModuleType(typeName, moduleScope, moduleBody); } public static IEnumerable CreateResourceProperties(ResourceTypeReference resourceTypeReference) diff --git a/src/Bicep.Core/TypeSystem/Az/AzResourceTypeFactory.cs b/src/Bicep.Core/TypeSystem/Az/AzResourceTypeFactory.cs index 6d3d836eb9c..211a0c3aa90 100644 --- a/src/Bicep.Core/TypeSystem/Az/AzResourceTypeFactory.cs +++ b/src/Bicep.Core/TypeSystem/Az/AzResourceTypeFactory.cs @@ -98,7 +98,7 @@ private TypeSymbol ToTypeSymbol(Azure.Bicep.Types.Concrete.TypeBase typeBase, bo var resourceTypeReference = ResourceTypeReference.Parse(resourceType.Name); var bodyType = GetTypeSymbol(resourceType.Body.Type, true); - return new ResourceType(resourceTypeReference, bodyType); + return new ResourceType(resourceTypeReference, ToResourceScope(resourceType.ScopeType), bodyType); } case Azure.Bicep.Types.Concrete.UnionType unionType: { @@ -146,5 +146,22 @@ private static TypeSymbolValidationFlags GetValidationFlags(bool isResourceBodyT // in all other places, we should allow some wiggle room so that we don't block compilation if there are any swagger inaccuracies return TypeSymbolValidationFlags.WarnOnTypeMismatch; } + + private static ResourceScope ToResourceScope(Azure.Bicep.Types.Concrete.ScopeType input) + { + if (input == Azure.Bicep.Types.Concrete.ScopeType.Unknown) + { + return ResourceScope.Tenant | ResourceScope.ManagementGroup | ResourceScope.Subscription | ResourceScope.ResourceGroup | ResourceScope.Resource; + } + + var output = ResourceScope.None; + output |= input.HasFlag(Azure.Bicep.Types.Concrete.ScopeType.Extension) ? ResourceScope.Resource : ResourceScope.None; + output |= input.HasFlag(Azure.Bicep.Types.Concrete.ScopeType.Tenant) ? ResourceScope.Tenant : ResourceScope.None; + output |= input.HasFlag(Azure.Bicep.Types.Concrete.ScopeType.ManagementGroup) ? ResourceScope.ManagementGroup : ResourceScope.None; + output |= input.HasFlag(Azure.Bicep.Types.Concrete.ScopeType.Subscription) ? ResourceScope.Subscription : ResourceScope.None; + output |= input.HasFlag(Azure.Bicep.Types.Concrete.ScopeType.ResourceGroup) ? ResourceScope.ResourceGroup : ResourceScope.None; + + return output; + } } } \ No newline at end of file diff --git a/src/Bicep.Core/TypeSystem/Az/AzResourceTypeProvider.cs b/src/Bicep.Core/TypeSystem/Az/AzResourceTypeProvider.cs index 3a64143e4ce..e4af27f069f 100644 --- a/src/Bicep.Core/TypeSystem/Az/AzResourceTypeProvider.cs +++ b/src/Bicep.Core/TypeSystem/Az/AzResourceTypeProvider.cs @@ -54,37 +54,34 @@ public AzResourceTypeProvider(ITypeLoader typeLoader) private ResourceType GenerateResourceType(ResourceTypeReference typeReference, bool isExistingResource) { - ResourceType resourceType; - Azure.Bicep.Types.Concrete.ScopeType serializedResourceScope; - if (availableResourceTypes.TryGetValue(typeReference, out var typeLocation)) { var serializedResourceType = typeLoader.LoadResourceType(typeLocation); - resourceType = resourceTypeFactory.GetResourceType(serializedResourceType); - serializedResourceScope = serializedResourceType.ScopeType; + var resourceType = resourceTypeFactory.GetResourceType(serializedResourceType); + + return SetBicepResourceProperties(resourceType, isExistingResource); } else { var resourceBodyType = new NamedObjectType(typeReference.FormatName(), TypeSymbolValidationFlags.Default, LanguageConstants.CreateResourceProperties(typeReference), null); - resourceType = new ResourceType(typeReference, resourceBodyType); - serializedResourceScope = Azure.Bicep.Types.Concrete.ScopeType.Unknown; - } + var resourceType = new ResourceType(typeReference, ResourceScope.Tenant | ResourceScope.ManagementGroup | ResourceScope.Subscription | ResourceScope.ResourceGroup | ResourceScope.Resource, resourceBodyType); - return SetBicepResourceProperties(resourceType, serializedResourceScope, isExistingResource); + return SetBicepResourceProperties(resourceType, isExistingResource); + } } - public static ResourceType SetBicepResourceProperties(ResourceType resourceType, Azure.Bicep.Types.Concrete.ScopeType resourceScope, bool isExistingResource) + public static ResourceType SetBicepResourceProperties(ResourceType resourceType, bool isExistingResource) { var bodyType = resourceType.Body.Type; switch (bodyType) { case ObjectType bodyObjectType: - bodyType = SetBicepResourceProperties(bodyObjectType, resourceScope, isExistingResource); + bodyType = SetBicepResourceProperties(bodyObjectType, resourceType.ValidParentScopes, isExistingResource); break; case DiscriminatedObjectType bodyDiscriminatedType: var bodyTypes = bodyDiscriminatedType.UnionMembersByKey.Values.ToList().Select(x => x.Type as ObjectType ?? throw new ArgumentException()); - bodyTypes = bodyTypes.Select(x => SetBicepResourceProperties(x, resourceScope, isExistingResource)); + bodyTypes = bodyTypes.Select(x => SetBicepResourceProperties(x, resourceType.ValidParentScopes, isExistingResource)); bodyType = new DiscriminatedObjectType( bodyDiscriminatedType.Name, bodyDiscriminatedType.ValidationFlags, @@ -95,16 +92,17 @@ public static ResourceType SetBicepResourceProperties(ResourceType resourceType, throw new ArgumentException(); } - return new ResourceType(resourceType.TypeReference, bodyType); + return new ResourceType(resourceType.TypeReference, resourceType.ValidParentScopes, bodyType); } - private static ObjectType SetBicepResourceProperties(ObjectType objectType, Azure.Bicep.Types.Concrete.ScopeType resourceScope, bool isExistingResource) + private static ObjectType SetBicepResourceProperties(ObjectType objectType, ResourceScope validParentScopes, bool isExistingResource) { var properties = objectType.Properties; var scopeRequiredFlag = TypePropertyFlags.WriteOnly; - if (resourceScope == Azure.Bicep.Types.Concrete.ScopeType.Extension) + if (validParentScopes == ResourceScope.Resource) { + // resource can only be deployed as an extension resource - scope should be required scopeRequiredFlag |= TypePropertyFlags.Required; } @@ -113,7 +111,7 @@ private static ObjectType SetBicepResourceProperties(ObjectType objectType, Azur if (isExistingResource) { // we can refer to a resource at any scope if it is an existing resource not being deployed by this file - var scopeReference = new ResourceScopeType(LanguageConstants.ResourceScopePropertyName, ConvertScopeType(resourceScope)); + var scopeReference = new ResourceScopeType(LanguageConstants.ResourceScopePropertyName, validParentScopes); properties = properties.SetItem(LanguageConstants.ResourceScopePropertyName, new TypeProperty(LanguageConstants.ResourceScopePropertyName, scopeReference, scopeRequiredFlag)); return new NamedObjectType( @@ -126,7 +124,7 @@ private static ObjectType SetBicepResourceProperties(ObjectType objectType, Azur else { // we only support scope for extension resources (or resources where the scope is unknown and thus may be an extension resource) - if (resourceScope.HasFlag(Azure.Bicep.Types.Concrete.ScopeType.Extension) || resourceScope == Azure.Bicep.Types.Concrete.ScopeType.Unknown) + if (validParentScopes.HasFlag(ResourceScope.Resource)) { var scopeReference = new ResourceScopeType(LanguageConstants.ResourceScopePropertyName, ResourceScope.Resource); properties = properties.SetItem(LanguageConstants.ResourceScopePropertyName, new TypeProperty(LanguageConstants.ResourceScopePropertyName, scopeReference, scopeRequiredFlag)); @@ -161,26 +159,6 @@ private static IEnumerable ConvertToReadOnly(IEnumerable (typePropertyFlags | TypePropertyFlags.ReadOnly) & ~TypePropertyFlags.Required; - private static ResourceScope ConvertScopeType(Azure.Bicep.Types.Concrete.ScopeType input) - { - var output = ResourceScope.None; - void AppendScopeType(Azure.Bicep.Types.Concrete.ScopeType scopeToCheck, ResourceScope scopeToAdd) - { - if (input == Azure.Bicep.Types.Concrete.ScopeType.Unknown || input.HasFlag(scopeToCheck)) - { - output |= scopeToAdd; - } - } - - AppendScopeType(Azure.Bicep.Types.Concrete.ScopeType.Extension, ResourceScope.Resource); - AppendScopeType(Azure.Bicep.Types.Concrete.ScopeType.Tenant, ResourceScope.Tenant); - AppendScopeType(Azure.Bicep.Types.Concrete.ScopeType.ManagementGroup, ResourceScope.ManagementGroup); - AppendScopeType(Azure.Bicep.Types.Concrete.ScopeType.Subscription, ResourceScope.Subscription); - AppendScopeType(Azure.Bicep.Types.Concrete.ScopeType.ResourceGroup, ResourceScope.ResourceGroup); - - return output; - } - public ResourceType GetType(ResourceTypeReference typeReference, bool isExistingResource) { var typeCache = isExistingResource ? loadedExistingTypeCache : loadedTypeCache; diff --git a/src/Bicep.Core/TypeSystem/ModuleType.cs b/src/Bicep.Core/TypeSystem/ModuleType.cs index 28b2507c0db..bd71500799e 100644 --- a/src/Bicep.Core/TypeSystem/ModuleType.cs +++ b/src/Bicep.Core/TypeSystem/ModuleType.cs @@ -5,14 +5,17 @@ namespace Bicep.Core.TypeSystem { public class ModuleType : TypeSymbol, IScopeReference { - public ModuleType(string name, ITypeReference body) + public ModuleType(string name, ResourceScope validParentScopes, ITypeReference body) : base(name) { + ValidParentScopes = validParentScopes; Body = body; } public override TypeKind TypeKind => TypeKind.Module; + public ResourceScope ValidParentScopes { get; } + public ITypeReference Body { get; } public ResourceScope Scope => ResourceScope.Module; diff --git a/src/Bicep.Core/TypeSystem/ResourceType.cs b/src/Bicep.Core/TypeSystem/ResourceType.cs index e4d0dca9a7e..6a1c656e283 100644 --- a/src/Bicep.Core/TypeSystem/ResourceType.cs +++ b/src/Bicep.Core/TypeSystem/ResourceType.cs @@ -6,10 +6,11 @@ namespace Bicep.Core.TypeSystem { public class ResourceType : TypeSymbol, IScopeReference { - public ResourceType(ResourceTypeReference typeReference, ITypeReference body) + public ResourceType(ResourceTypeReference typeReference, ResourceScope validParentScopes, ITypeReference body) : base(typeReference.FormatName()) { TypeReference = typeReference; + ValidParentScopes = validParentScopes; Body = body; } @@ -17,6 +18,8 @@ public ResourceType(ResourceTypeReference typeReference, ITypeReference body) public ResourceTypeReference TypeReference { get; } + public ResourceScope ValidParentScopes { get; } + public ITypeReference Body { get; } public ResourceScope Scope => ResourceScope.Resource; diff --git a/src/Bicep.Core/TypeSystem/TypeValidator.cs b/src/Bicep.Core/TypeSystem/TypeValidator.cs index 1e3f653c287..f939f316a38 100644 --- a/src/Bicep.Core/TypeSystem/TypeValidator.cs +++ b/src/Bicep.Core/TypeSystem/TypeValidator.cs @@ -149,14 +149,14 @@ private static TypeSymbol NarrowTypeInternal(ITypeManager typeManager, // When assigning a resource, we're really assigning the value of the resource body. var narrowedBody = NarrowTypeInternal(typeManager, expression, targetResourceType.Body.Type, diagnosticWriter, typeMismatchErrorFactory, skipConstantCheck, skipTypeErrors); - return new ResourceType(targetResourceType.TypeReference, narrowedBody); + return new ResourceType(targetResourceType.TypeReference, targetResourceType.ValidParentScopes, narrowedBody); } if (targetType is ModuleType targetModuleType) { var narrowedBody = NarrowTypeInternal(typeManager, expression, targetModuleType.Body.Type, diagnosticWriter, typeMismatchErrorFactory, skipConstantCheck, skipTypeErrors); - return new ModuleType(targetModuleType.Name, narrowedBody); + return new ModuleType(targetModuleType.Name, targetModuleType.ValidParentScopes, narrowedBody); } // TODO: The type of this expression and all subexpressions should be cached diff --git a/src/Bicep.Decompiler.IntegrationTests/Working/modules/main.bicep b/src/Bicep.Decompiler.IntegrationTests/Working/modules/main.bicep index 4de75ad3dcb..7a3c5ca8f77 100644 --- a/src/Bicep.Decompiler.IntegrationTests/Working/modules/main.bicep +++ b/src/Bicep.Decompiler.IntegrationTests/Working/modules/main.bicep @@ -62,7 +62,7 @@ module moduleWithRgAndSub 'nested/module1.bicep' = { module moduleWithSub 'nested/module1.bicep' = { name: 'moduleWithSub' scope: subscription('${module1Url}test') -//@[9:42) [BCP123 (Error)] The supplied scope "subscription" is not valid for this module. Permitted scopes: "resourceGroup". |subscription('${module1Url}test')| +//@[9:42) [BCP123 (Error)] Scope "subscription" is not valid for this module. Permitted scopes: "resourceGroup". |subscription('${module1Url}test')| params: {} //@[2:8) [BCP035 (Error)] The specified "object" declaration is missing the following required properties: "arrayParam", "location", "objectParam". |params| }