From 92dea0a5b604197eacfb6dfdb9b7c75a7438e4fb Mon Sep 17 00:00:00 2001 From: Mikolaj Mackowiak <7921224+miqm@users.noreply.github.com> Date: Wed, 22 Sep 2021 23:36:53 +0200 Subject: [PATCH 1/4] Fix using index value in module scope --- .../Scenarios/ScopeTests.cs | 177 ++++++++++++++++++ src/Bicep.Core/Emit/ExpressionEmitter.cs | 17 +- src/Bicep.Core/Emit/ScopeHelper.cs | 45 ++--- src/Bicep.Core/Emit/TemplateWriter.cs | 8 +- src/Bicep.Core/Semantics/Decorator.cs | 9 +- 5 files changed, 223 insertions(+), 33 deletions(-) create mode 100644 src/Bicep.Core.IntegrationTests/Scenarios/ScopeTests.cs diff --git a/src/Bicep.Core.IntegrationTests/Scenarios/ScopeTests.cs b/src/Bicep.Core.IntegrationTests/Scenarios/ScopeTests.cs new file mode 100644 index 00000000000..09d768f4659 --- /dev/null +++ b/src/Bicep.Core.IntegrationTests/Scenarios/ScopeTests.cs @@ -0,0 +1,177 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Bicep.Core.UnitTests.Assertions; +using Bicep.Core.UnitTests.Utils; +using FluentAssertions.Execution; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json.Linq; + +namespace Bicep.Core.IntegrationTests.Scenarios +{ + [TestClass] + public class ScopeTests + { + [TestMethod] + public void UsingIndexValueInModuleScope_ShouldInlineTheResourceGroupName() + { + var result = CompilationHelper.Compile(("main.bicep", @" +targetScope = 'subscription' + +// parameters +param tags object +param location string + +// variables +var resourceGroups = [ + 'test-uat-rg' + 'test-uat-blue-rg' + 'test-uat-green-rg' +] + +// base resource group deployment +resource rgs 'Microsoft.Resources/resourceGroups@2021-04-01' = [for rgname in resourceGroups: { + name: rgname + location: location + tags: tags +}] + +module storage 'storage.bicep' = { + name: 'str' + scope: rgs[0] + params: { + environment: 'uat' + } +} +"), ("storage.bicep", @" +param environment string +output env string = environment +")); + + result.Should().NotHaveAnyDiagnostics(); + using (new AssertionScope()) + { + result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'str')].resourceGroup", "[variables('resourceGroups')[0]]"); + } + } + + [TestMethod] + public void UsingIndexValueInModuleScope_ShouldInlineTheManagementGroupName_1() + { + var result = CompilationHelper.Compile(("main.bicep", @" +targetScope = 'tenant' + +// variables +var managementGroups = [ + 'mg1' + 'mg2' +] + +resource mgs 'Microsoft.Management/managementGroups@2020-02-01' = [for mgname in managementGroups: { + name: mgname +}] + + +module mod 'mod.bicep' = { + name: 'mod' + scope: mgs[0] + params: { + environment: 'uat' + } +} +"), ("mod.bicep", @" +targetScope = 'managementGroup' +param environment string +output env string = environment +")); + + result.Should().NotHaveAnyDiagnostics(); + using (new AssertionScope()) + { + result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'mod')].scope", "[format('Microsoft.Management/managementGroups/{0}', variables('managementGroups')[0])]"); + } + } + + [TestMethod] + public void UsingIndexValueInModuleScope_ShouldInlineTheManagementGroupName_2() + { + var result = CompilationHelper.Compile(("main.bicep", @" +targetScope = 'tenant' + +// variables +var managementGroups = [ + 'mg1' + 'mg2' +] + +resource mgs 'Microsoft.Management/managementGroups@2020-02-01' existing = [for mgname in managementGroups: { + name: mgname +}] + + +module mod 'mod.bicep' = { + name: 'mod' + scope: mgs[0] + params: { + environment: 'uat' + } +} +"), ("mod.bicep", @" +targetScope = 'managementGroup' +param environment string +output env string = environment +")); + + result.Should().NotHaveAnyDiagnostics(); + using (new AssertionScope()) + { + result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'mod')].scope", "[format('Microsoft.Management/managementGroups/{0}', variables('managementGroups')[0])]"); + } + } + + [TestMethod] + public void UsingIndexValueInModuleScope_ShouldInlineTheResourceGroupAndSubscription() + { + var result = CompilationHelper.Compile(("main.bicep", @" +targetScope = 'subscription' + +// variables +var resourceGroups = [ + { + name: 'test-uat-rg' + sub: '00000000-0000-0000-0000-000000000000' + } +] + +// base resource group deployment +resource rgs 'Microsoft.Resources/resourceGroups@2021-04-01' existing = [for rg in resourceGroups: { + name: rg.name + scope: subscription(rg.sub) +}] + +module storage 'storage.bicep' = { + name: 'str' + scope: rgs[0] + params: { + environment: 'uat' + } +} +"), ("storage.bicep", @" +param environment string +output env string = environment +")); + + result.Should().NotHaveAnyDiagnostics(); + using (new AssertionScope()) + { + result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'str')].resourceGroup", "[variables('resourceGroups')[0].name]"); + result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'str')].subscriptionId", "[variables('resourceGroups')[0].sub]"); + } + } + } +} diff --git a/src/Bicep.Core/Emit/ExpressionEmitter.cs b/src/Bicep.Core/Emit/ExpressionEmitter.cs index d009f818bb8..8a9beda7ecb 100644 --- a/src/Bicep.Core/Emit/ExpressionEmitter.cs +++ b/src/Bicep.Core/Emit/ExpressionEmitter.cs @@ -91,6 +91,16 @@ public void EmitExpression(SyntaxBase syntax) } } + public void EmitResourceName(SyntaxBase resourceNameSyntax, SyntaxBase? indexExpression, SyntaxBase newContext) + { + var converterForContext = converter.CreateConverterForIndexReplacement(resourceNameSyntax, indexExpression, newContext); + + var unqualifiedResourceId = converterForContext.ConvertExpression(resourceNameSyntax); + var serialized = ExpressionSerializer.SerializeExpression(unqualifiedResourceId); + + writer.WriteValue(serialized); + } + public void EmitUnqualifiedResourceId(ResourceMetadata resource, SyntaxBase? indexExpression, SyntaxBase newContext) { var converterForContext = converter.CreateConverterForIndexReplacement(resource.NameSyntax, indexExpression, newContext); @@ -142,8 +152,11 @@ public LanguageExpression GetFullyQualifiedResourceName(ResourceMetadata resourc return converter.GetFullyQualifiedResourceName(resource); } - public LanguageExpression GetManagementGroupResourceId(SyntaxBase managementGroupNameProperty, bool fullyQualified) - => converter.GenerateManagementGroupResourceId(managementGroupNameProperty, fullyQualified); + public LanguageExpression GetManagementGroupResourceId(SyntaxBase managementGroupNameProperty, SyntaxBase? indexExpression, SyntaxBase newContext, bool fullyQualified) + { + var converterForContext = converter.CreateConverterForIndexReplacement(managementGroupNameProperty, indexExpression, newContext); + return converterForContext.GenerateManagementGroupResourceId(managementGroupNameProperty, fullyQualified); + } public void EmitLanguageExpression(SyntaxBase syntax) { diff --git a/src/Bicep.Core/Emit/ScopeHelper.cs b/src/Bicep.Core/Emit/ScopeHelper.cs index 6225aa0f654..bb2c240ca7e 100644 --- a/src/Bicep.Core/Emit/ScopeHelper.cs +++ b/src/Bicep.Core/Emit/ScopeHelper.cs @@ -74,7 +74,7 @@ public class ScopeData // all other scope expressions _ => (semanticModel.GetSymbolInfo(scopeValue), null) }; - + var scopeType = semanticModel.GetTypeInfo(scopeValue); switch (scopeType) @@ -128,7 +128,7 @@ public class ScopeData _ => new ScopeData { RequestedScope = ResourceScope.ResourceGroup, SubscriptionIdProperty = type.Arguments[0].Expression, ResourceGroupProperty = type.Arguments[1].Expression, IndexExpression = indexExpression }, }; case { } when scopeSymbol is ResourceSymbol targetResourceSymbol: - if (semanticModel.ResourceMetadata.TryLookup(targetResourceSymbol.DeclaringSyntax) is not {} targetResource) + if (semanticModel.ResourceMetadata.TryLookup(targetResourceSymbol.DeclaringSyntax) is not { } targetResource) { return null; } @@ -193,7 +193,7 @@ public class ScopeData // otherwise, the errors produced by the type check are sufficient logInvalidScopeFunc(scopeValue, ResourceScope.Module, supportedScopes); } - + return null; } @@ -260,7 +260,7 @@ public static LanguageExpression FormatFullyQualifiedResourceId(EmitterContext c // but until we have it, we should generate unqualified resource Ids. There should not be a risk of collision, because we do not allow mixing of resource scopes in a single bicep file. return ExpressionConverter.GenerateUnqualifiedResourceId(fullyQualifiedType, nameSegments); case ResourceScope.Resource: - if (scopeData.ResourceScope is not {} resource) + if (scopeData.ResourceScope is not { } resource) { throw new InvalidOperationException("Cannot format resourceId with non-null resource scope symbol"); } @@ -291,7 +291,7 @@ public static LanguageExpression FormatUnqualifiedResourceId(EmitterContext cont case ResourceScope.ManagementGroup: return ExpressionConverter.GenerateUnqualifiedResourceId(fullyQualifiedType, nameSegments); case ResourceScope.Resource: - if (scopeData.ResourceScope is not {} resource) + if (scopeData.ResourceScope is not { } resource) { throw new InvalidOperationException("Cannot format resourceId with non-null resource scope symbol"); } @@ -314,7 +314,7 @@ public static LanguageExpression FormatUnqualifiedResourceId(EmitterContext cont public static void EmitResourceScopeProperties(SemanticModel semanticModel, ScopeData scopeData, ExpressionEmitter expressionEmitter, SyntaxBase newContext) { - if (scopeData.ResourceScope is {} scopeResource) + if (scopeData.ResourceScope is { } scopeResource) { // emit the resource id of the resource being extended expressionEmitter.EmitProperty("scope", () => expressionEmitter.EmitUnqualifiedResourceId(scopeResource, scopeData.IndexExpression, newContext)); @@ -326,7 +326,7 @@ public static void EmitResourceScopeProperties(SemanticModel semanticModel, Scop } } - public static void EmitModuleScopeProperties(ResourceScope targetScope, ScopeData scopeData, ExpressionEmitter expressionEmitter) + public static void EmitModuleScopeProperties(ResourceScope targetScope, ScopeData scopeData, ExpressionEmitter expressionEmitter, SyntaxBase newContext) { switch (scopeData.RequestedScope) { @@ -334,15 +334,15 @@ public static void EmitModuleScopeProperties(ResourceScope targetScope, ScopeDat expressionEmitter.EmitProperty("scope", new JTokenExpression("/")); return; case ResourceScope.ManagementGroup: - if (scopeData.ManagementGroupNameProperty != null) + if (scopeData.ManagementGroupNameProperty is not null) { // The template engine expects an unqualified resourceId for the management group scope if deploying at tenant or management group scope var useFullyQualifiedResourceId = targetScope != ResourceScope.Tenant && targetScope != ResourceScope.ManagementGroup; - expressionEmitter.EmitProperty("scope", expressionEmitter.GetManagementGroupResourceId(scopeData.ManagementGroupNameProperty, useFullyQualifiedResourceId)); + expressionEmitter.EmitProperty("scope", expressionEmitter.GetManagementGroupResourceId(scopeData.ManagementGroupNameProperty, scopeData.IndexExpression, newContext, useFullyQualifiedResourceId)); } return; case ResourceScope.Subscription: - if (scopeData.SubscriptionIdProperty != null) + if (scopeData.SubscriptionIdProperty is not null) { expressionEmitter.EmitProperty("subscriptionId", scopeData.SubscriptionIdProperty); } @@ -352,13 +352,13 @@ public static void EmitModuleScopeProperties(ResourceScope targetScope, ScopeDat } return; case ResourceScope.ResourceGroup: - if (scopeData.SubscriptionIdProperty != null) + if (scopeData.SubscriptionIdProperty is not null) { - expressionEmitter.EmitProperty("subscriptionId", scopeData.SubscriptionIdProperty); + expressionEmitter.EmitProperty("subscriptionId", () => expressionEmitter.EmitResourceName(scopeData.SubscriptionIdProperty, scopeData.IndexExpression, newContext)); } - if (scopeData.ResourceGroupProperty != null) + if (scopeData.ResourceGroupProperty is not null) { - expressionEmitter.EmitProperty("resourceGroup", scopeData.ResourceGroupProperty); + expressionEmitter.EmitProperty("resourceGroup", () => expressionEmitter.EmitResourceName(scopeData.ResourceGroupProperty, scopeData.IndexExpression, newContext)); } return; default: @@ -413,7 +413,7 @@ private static void ValidateResourceScopeRestrictions(SemanticModel semanticMode { return; } - + var rootResource = GetRootResource(scopeInfo, resource); if (rootResource is null || !scopeInfo.TryGetValue(rootResource, out var scopeData)) @@ -438,7 +438,7 @@ private static bool IsDeployableResourceScope(SemanticModel semanticModel, Scope // we only allow resources to be deployed at the target scope var matchesTargetScope = (scopeData.RequestedScope == semanticModel.TargetScope && - scopeData.ManagementGroupNameProperty is null && + scopeData.ManagementGroupNameProperty is null && scopeData.SubscriptionIdProperty is null && scopeData.ResourceGroupProperty is null && scopeData.ResourceScope is null); @@ -475,7 +475,7 @@ void logInvalidScopeDiagnostic(IPositionable positionable, ResourceScope supplie } var firstAncestor = ancestors.First(); - if (!resource.IsExistingResource && + if (!resource.IsExistingResource && firstAncestor.Resource.IsExistingResource && !IsDeployableResourceScope(semanticModel, scopeInfo[firstAncestor.Resource])) { @@ -521,12 +521,13 @@ private static void ValidateNestedTemplateScopeRestrictions(SemanticModel semant bool checkScopes(params ResourceScope[] scopes) => scopes.Contains(semanticModel.TargetScope); - var isValid = scopeData.RequestedScope switch { + var isValid = scopeData.RequestedScope switch + { // If you update this switch block to add new supported nested template scope combinations, // please ensure you update the wording of error messages BCP113, BCP114, BCP115 & BCP116 to reflect this! - ResourceScope.Tenant + ResourceScope.Tenant => checkScopes(ResourceScope.Tenant, ResourceScope.ManagementGroup, ResourceScope.Subscription, ResourceScope.ResourceGroup), - ResourceScope.ManagementGroup when scopeData.ManagementGroupNameProperty is not null + ResourceScope.ManagementGroup when scopeData.ManagementGroupNameProperty is not null => checkScopes(ResourceScope.Tenant, ResourceScope.ManagementGroup), ResourceScope.ManagementGroup => checkScopes(ResourceScope.Tenant, ResourceScope.ManagementGroup), @@ -542,7 +543,7 @@ ResourceScope.ResourceGroup when scopeData.ResourceGroupProperty is not null => checkScopes(ResourceScope.ResourceGroup), _ => false, }; - + if (isValid) { return; @@ -574,7 +575,7 @@ void LogInvalidScopeDiagnostic(IPositionable positionable, ResourceScope supplie foreach (var moduleSymbol in semanticModel.Root.ModuleDeclarations) { - if (moduleSymbol.TryGetModuleType() is not {} moduleType) + if (moduleSymbol.TryGetModuleType() is not { } moduleType) { // missing type should be caught during type validation continue; diff --git a/src/Bicep.Core/Emit/TemplateWriter.cs b/src/Bicep.Core/Emit/TemplateWriter.cs index f1be0a08400..d38f1a8b576 100644 --- a/src/Bicep.Core/Emit/TemplateWriter.cs +++ b/src/Bicep.Core/Emit/TemplateWriter.cs @@ -194,7 +194,11 @@ private ObjectSyntax AddDecoratorsToBody(StatementSyntax statement, ObjectSyntax .SelectMany(ns => ns.Value.Type.DecoratorResolver.GetMatches(decoratorSymbol, argumentTypes)) .Single(); - result = decorator.Evaluate(decoratorSyntax, targetType, result); + var evaluated = decorator.Evaluate(decoratorSyntax, targetType, result); + if (evaluated is not null) + { + result = evaluated; + } } } @@ -527,7 +531,7 @@ private void EmitModule(JsonTextWriter jsonWriter, ModuleSymbol moduleSymbol, Ex emitter.EmitObjectProperties((ObjectSyntax)body, ModulePropertiesToOmit); var scopeData = context.ModuleScopeData[moduleSymbol]; - ScopeHelper.EmitModuleScopeProperties(context.SemanticModel.TargetScope, scopeData, emitter); + ScopeHelper.EmitModuleScopeProperties(context.SemanticModel.TargetScope, scopeData, emitter, body); if (scopeData.RequestedScope != ResourceScope.ResourceGroup) { diff --git a/src/Bicep.Core/Semantics/Decorator.cs b/src/Bicep.Core/Semantics/Decorator.cs index 578f0443754..f610e859e53 100644 --- a/src/Bicep.Core/Semantics/Decorator.cs +++ b/src/Bicep.Core/Semantics/Decorator.cs @@ -51,13 +51,8 @@ public void Validate(DecoratorSyntax decoratorSyntax, TypeSymbol targetType, ITy diagnosticWriter.Write(DiagnosticBuilder.ForPosition(decoratorSyntax).CannotAttachDecoratorToTarget(this.Overload.Name, attachableType, targetType)); } - // Custom validator provided. - if (this.validator != null) - { - this.validator.Invoke(this.Overload.Name, decoratorSyntax, targetType, typeManager, binder, diagnosticWriter); - - return; - } + // Invoke custom validator if provided. + this.validator?.Invoke(this.Overload.Name, decoratorSyntax, targetType, typeManager, binder, diagnosticWriter); } public ObjectSyntax? Evaluate(DecoratorSyntax decoratorSyntax, TypeSymbol targetType, ObjectSyntax? targetObject) From c61df1d64232a47912d2abdaa65a667d977cf08c Mon Sep 17 00:00:00 2001 From: Mikolaj Mackowiak <7921224+miqm@users.noreply.github.com> Date: Tue, 28 Sep 2021 22:00:24 +0200 Subject: [PATCH 2/4] Fix scopes to looped child resources --- .../Scenarios/ScopeTests.cs | 49 +++++++++++++++++++ src/Bicep.Core/Emit/ScopeHelper.cs | 1 + 2 files changed, 50 insertions(+) diff --git a/src/Bicep.Core.IntegrationTests/Scenarios/ScopeTests.cs b/src/Bicep.Core.IntegrationTests/Scenarios/ScopeTests.cs index 09d768f4659..c4896253417 100644 --- a/src/Bicep.Core.IntegrationTests/Scenarios/ScopeTests.cs +++ b/src/Bicep.Core.IntegrationTests/Scenarios/ScopeTests.cs @@ -173,5 +173,54 @@ param environment string result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'str')].subscriptionId", "[variables('resourceGroups')[0].sub]"); } } + [TestMethod] + public void ScopeToNestedChildResource_ShouldGenerateScopeIdCorrectly() + { + var result = CompilationHelper.Compile(("main.bicep", @" +param postgreSqlServerId string +var PSQL_DATABASES = [ +{ + database: { + name: 'db1' + } +} +{ + database: { + name: 'db2' + } +} +] + +resource postgreSQL 'Microsoft.DBForPostgreSQL/servers@2017-12-01' existing = { + name: last(split(postgreSqlServerId, '/')) + resource database 'databases' = [for (item, index) in PSQL_DATABASES: { + name: item.database.name + properties: { + charset: contains(item.database, 'charset') ? item.database.charset : 'utf8' + collation: contains(item.database, 'collation') ? item.database.charset : 'English_United States.1252' + } + }] +} + +resource dbLocks 'Microsoft.Authorization/locks@2016-09-01' = [for (item, index) in PSQL_DATABASES: { + name: 'dbLock' + scope: postgreSQL::database[index] + properties: { + level: 'CanNotDelete' + notes: 'Database cannot be deleted' + } +}] +")); + + result.Should().NotHaveAnyDiagnostics(); + using (new AssertionScope()) + { + result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'dbLock')].scope", "[format('Microsoft.DBforPostgreSQL/servers/{0}/databases/{1}', last(split(parameters('postgreSqlServerId'), '/')), variables('PSQL_DATABASES')[copyIndex()].database.name)]"); + result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'dbLock')].dependsOn", new JArray + { + "[resourceId('Microsoft.DBforPostgreSQL/servers/databases', last(split(parameters('postgreSqlServerId'), '/')), variables('PSQL_DATABASES')[copyIndex()].database.name)]" + }); + } + } } } diff --git a/src/Bicep.Core/Emit/ScopeHelper.cs b/src/Bicep.Core/Emit/ScopeHelper.cs index bb2c240ca7e..dc8cdca7012 100644 --- a/src/Bicep.Core/Emit/ScopeHelper.cs +++ b/src/Bicep.Core/Emit/ScopeHelper.cs @@ -70,6 +70,7 @@ public class ScopeData { // scope indexing can only happen with references to module or resource collections ArrayAccessSyntax { BaseExpression: VariableAccessSyntax baseVariableAccess } arrayAccess => (semanticModel.GetSymbolInfo(baseVariableAccess), arrayAccess.IndexExpression), + ArrayAccessSyntax { BaseExpression: ResourceAccessSyntax baseVariableAccess } arrayAccess => (semanticModel.GetSymbolInfo(baseVariableAccess), arrayAccess.IndexExpression), // all other scope expressions _ => (semanticModel.GetSymbolInfo(scopeValue), null) From c0daff18f82dd352d7940e5c6486fadfd6eaba10 Mon Sep 17 00:00:00 2001 From: Mikolaj Mackowiak <7921224+miqm@users.noreply.github.com> Date: Tue, 28 Sep 2021 22:28:02 +0200 Subject: [PATCH 3/4] Fix variable name --- src/Bicep.Core/Emit/ExpressionEmitter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bicep.Core/Emit/ExpressionEmitter.cs b/src/Bicep.Core/Emit/ExpressionEmitter.cs index 8a9beda7ecb..ba9fc2c4eac 100644 --- a/src/Bicep.Core/Emit/ExpressionEmitter.cs +++ b/src/Bicep.Core/Emit/ExpressionEmitter.cs @@ -95,8 +95,8 @@ public void EmitResourceName(SyntaxBase resourceNameSyntax, SyntaxBase? indexExp { var converterForContext = converter.CreateConverterForIndexReplacement(resourceNameSyntax, indexExpression, newContext); - var unqualifiedResourceId = converterForContext.ConvertExpression(resourceNameSyntax); - var serialized = ExpressionSerializer.SerializeExpression(unqualifiedResourceId); + var resourceName = converterForContext.ConvertExpression(resourceNameSyntax); + var serialized = ExpressionSerializer.SerializeExpression(resourceName); writer.WriteValue(serialized); } From ef5d5481b2c474761fc7715e6dad2d4dc38fc824 Mon Sep 17 00:00:00 2001 From: Mikolaj Mackowiak <7921224+miqm@users.noreply.github.com> Date: Tue, 28 Sep 2021 22:58:21 +0200 Subject: [PATCH 4/4] Renamed expression emitting function --- src/Bicep.Core/Emit/ExpressionEmitter.cs | 6 +++--- src/Bicep.Core/Emit/ScopeHelper.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Bicep.Core/Emit/ExpressionEmitter.cs b/src/Bicep.Core/Emit/ExpressionEmitter.cs index ba9fc2c4eac..1ecfad227ef 100644 --- a/src/Bicep.Core/Emit/ExpressionEmitter.cs +++ b/src/Bicep.Core/Emit/ExpressionEmitter.cs @@ -91,12 +91,12 @@ public void EmitExpression(SyntaxBase syntax) } } - public void EmitResourceName(SyntaxBase resourceNameSyntax, SyntaxBase? indexExpression, SyntaxBase newContext) + public void EmitExpression(SyntaxBase resourceNameSyntax, SyntaxBase? indexExpression, SyntaxBase newContext) { var converterForContext = converter.CreateConverterForIndexReplacement(resourceNameSyntax, indexExpression, newContext); - var resourceName = converterForContext.ConvertExpression(resourceNameSyntax); - var serialized = ExpressionSerializer.SerializeExpression(resourceName); + var expression = converterForContext.ConvertExpression(resourceNameSyntax); + var serialized = ExpressionSerializer.SerializeExpression(expression); writer.WriteValue(serialized); } diff --git a/src/Bicep.Core/Emit/ScopeHelper.cs b/src/Bicep.Core/Emit/ScopeHelper.cs index dc8cdca7012..b5f2a9c5f7b 100644 --- a/src/Bicep.Core/Emit/ScopeHelper.cs +++ b/src/Bicep.Core/Emit/ScopeHelper.cs @@ -355,11 +355,11 @@ public static void EmitModuleScopeProperties(ResourceScope targetScope, ScopeDat case ResourceScope.ResourceGroup: if (scopeData.SubscriptionIdProperty is not null) { - expressionEmitter.EmitProperty("subscriptionId", () => expressionEmitter.EmitResourceName(scopeData.SubscriptionIdProperty, scopeData.IndexExpression, newContext)); + expressionEmitter.EmitProperty("subscriptionId", () => expressionEmitter.EmitExpression(scopeData.SubscriptionIdProperty, scopeData.IndexExpression, newContext)); } if (scopeData.ResourceGroupProperty is not null) { - expressionEmitter.EmitProperty("resourceGroup", () => expressionEmitter.EmitResourceName(scopeData.ResourceGroupProperty, scopeData.IndexExpression, newContext)); + expressionEmitter.EmitProperty("resourceGroup", () => expressionEmitter.EmitExpression(scopeData.ResourceGroupProperty, scopeData.IndexExpression, newContext)); } return; default: