From 935301a43b52c69884112e0ff4367a9e87992328 Mon Sep 17 00:00:00 2001 From: Anthony Martin <38542602+anthony-c-martin@users.noreply.github.com> Date: Fri, 31 Jan 2025 07:08:17 -0500 Subject: [PATCH] Ensure implicit extensions are output --- .../ExtensionRegistryTests.cs | 38 +++++++++++++++++++ src/Bicep.Core/Emit/TemplateWriter.cs | 10 ++--- src/Bicep.Core/Intermediate/Expression.cs | 6 +-- .../Intermediate/ExpressionBuilder.cs | 18 +++++++-- .../Intermediate/ExpressionRewriteVisitor.cs | 4 +- .../Intermediate/ExpressionVisitor.cs | 2 +- .../Intermediate/IExpressionVisitor.cs | 2 +- 7 files changed, 64 insertions(+), 16 deletions(-) diff --git a/src/Bicep.Core.IntegrationTests/ExtensionRegistryTests.cs b/src/Bicep.Core.IntegrationTests/ExtensionRegistryTests.cs index 7cbd9b7c305..092e31649ad 100644 --- a/src/Bicep.Core.IntegrationTests/ExtensionRegistryTests.cs +++ b/src/Bicep.Core.IntegrationTests/ExtensionRegistryTests.cs @@ -722,4 +722,42 @@ extension foo result.Should().GenerateATemplate(); } + + [TestMethod] + public async Task Implicit_extensions_are_included_in_output() + { + // https://github.com/Azure/bicep/issues/15395 + var fileSystem = new MockFileSystem(); + var services = await ExtensionTestHelper.GetServiceBuilderWithPublishedExtension(ThirdPartyTypeHelper.GetTestTypesTgz(), AllFeaturesEnabled, fileSystem); + + fileSystem.File.WriteAllText("/bicepconfig.json", """ +{ + "extensions": { + "foo": "br:example.azurecr.io/extensions/foo:1.2.3" + }, + "implicitExtensions": ["foo"], + "experimentalFeaturesEnabled": { + "extensibility": true + } +} +"""); + var result = await CompilationHelper.RestoreAndCompile(services, """ +resource fooRes 'fooType@v1' = { + identifier: 'foo' + properties: { + required: 'bar' + } +} +"""); + + result.Should().GenerateATemplate(); + result.Template.Should().HaveJsonAtPath("$.imports", """ +{ + "foo": { + "provider": "ThirdPartyExtension", + "version": "1.0.0" + } +} +"""); + } } diff --git a/src/Bicep.Core/Emit/TemplateWriter.cs b/src/Bicep.Core/Emit/TemplateWriter.cs index afdba68f91b..a22e0d29561 100644 --- a/src/Bicep.Core/Emit/TemplateWriter.cs +++ b/src/Bicep.Core/Emit/TemplateWriter.cs @@ -1021,7 +1021,7 @@ private void EmitVariablesIfPresent(ExpressionEmitter emitter, IEnumerable extensions) + private void EmitExtensionsIfPresent(ExpressionEmitter emitter, ImmutableArray extensions) { if (!extensions.Any()) { @@ -1043,7 +1043,7 @@ private void EmitExtensionsIfPresent(ExpressionEmitter emitter, ImmutableArray extensions) + private static void EmitProviders(ExpressionEmitter emitter, ImmutableArray extensions) { emitter.EmitObjectProperty("imports", () => { @@ -1064,7 +1064,7 @@ private static void EmitProviders(ExpressionEmitter emitter, ImmutableArray extensions) + private static void EmitExtensions(ExpressionEmitter emitter, ImmutableArray extensions) { emitter.EmitObjectProperty("extensions", () => { @@ -1084,7 +1084,7 @@ private static void EmitExtensions(ExpressionEmitter emitter, ImmutableArray new { Name }; } -public record DeclaredExtensionExpression( +public record ExtensionExpression( SyntaxBase? SourceSyntax, string Name, NamespaceSettings Settings, @@ -396,7 +396,7 @@ public record DeclaredExtensionExpression( ) : DescribableExpression(SourceSyntax, Description) { public override void Accept(IExpressionVisitor visitor) - => visitor.VisitDeclaredExtensionExpression(this); + => visitor.VisitExtensionExpression(this); protected override object? GetDebugAttributes() => new { Name }; } @@ -525,7 +525,7 @@ public override void Accept(IExpressionVisitor visitor) public record ProgramExpression( SyntaxBase? SourceSyntax, ImmutableArray Metadata, - ImmutableArray Extensions, + ImmutableArray Extensions, ImmutableArray Types, ImmutableArray Parameters, ImmutableArray Variables, diff --git a/src/Bicep.Core/Intermediate/ExpressionBuilder.cs b/src/Bicep.Core/Intermediate/ExpressionBuilder.cs index b46784587dc..f4beca8fd1b 100644 --- a/src/Bicep.Core/Intermediate/ExpressionBuilder.cs +++ b/src/Bicep.Core/Intermediate/ExpressionBuilder.cs @@ -169,7 +169,7 @@ private Expression ConvertWithoutLowering(SyntaxBase syntax) case ExtensionDeclarationSyntax extension: var symbol = GetDeclaredSymbol(extension); - return EvaluateDecorators(extension, new DeclaredExtensionExpression( + return EvaluateDecorators(extension, new ExtensionExpression( extension, symbol.Name, GetTypeInfo(extension).Settings, @@ -448,9 +448,19 @@ private ProgramExpression ConvertProgram(ProgramSyntax syntax) .OfType() .ToImmutableArray(); - var extensions = Context.SemanticModel.Root.ExtensionDeclarations + var declaredExtensions = Context.SemanticModel.Root.ExtensionDeclarations .Select(x => ConvertWithoutLowering(x.DeclaringSyntax)) - .OfType() + .OfType() + .ToImmutableArray(); + + var implicitExtension = Context.SemanticModel.Binder.NamespaceResolver.ImplicitNamespaces + .Select(x => x.Value.TryGetNamespaceType()) + .WhereNotNull() + .Where(x => + // The 'az' and 'sys' namespaces do not utilize the extensibility contract and do not need to be emitted in the template + !LanguageConstants.IdentifierComparer.Equals(x.Settings.BicepExtensionName, SystemNamespaceType.BuiltInName) && + !LanguageConstants.IdentifierComparer.Equals(x.Settings.BicepExtensionName, AzNamespaceType.BuiltInName)) + .Select(x => new ExtensionExpression(null, x.Name, x.Settings, null)) .ToImmutableArray(); var typeDefinitions = Context.SemanticModel.Root.TypeDeclarations @@ -502,7 +512,7 @@ private ProgramExpression ConvertProgram(ProgramSyntax syntax) return new ProgramExpression( syntax, metadataArray, - extensions, + [.. declaredExtensions, .. implicitExtension], typeDefinitions, parameters, functionVariables.AddRange(variables), diff --git a/src/Bicep.Core/Intermediate/ExpressionRewriteVisitor.cs b/src/Bicep.Core/Intermediate/ExpressionRewriteVisitor.cs index 8cc4bb570cf..7dcea54be08 100644 --- a/src/Bicep.Core/Intermediate/ExpressionRewriteVisitor.cs +++ b/src/Bicep.Core/Intermediate/ExpressionRewriteVisitor.cs @@ -246,8 +246,8 @@ public virtual Expression ReplaceDeclaredMetadataExpression(DeclaredMetadataExpr return hasChanges ? expression with { Value = value, Description = description } : expression; } - void IExpressionVisitor.VisitDeclaredExtensionExpression(DeclaredExtensionExpression expression) => ReplaceCurrent(expression, ReplaceDeclaredExtensionExpression); - public virtual Expression ReplaceDeclaredExtensionExpression(DeclaredExtensionExpression expression) + void IExpressionVisitor.VisitExtensionExpression(ExtensionExpression expression) => ReplaceCurrent(expression, ReplaceExtensionExpression); + public virtual Expression ReplaceExtensionExpression(ExtensionExpression expression) { var hasChanges = TryRewrite(expression.Config, out var config) | diff --git a/src/Bicep.Core/Intermediate/ExpressionVisitor.cs b/src/Bicep.Core/Intermediate/ExpressionVisitor.cs index 7d5db747f78..e8f645a9521 100644 --- a/src/Bicep.Core/Intermediate/ExpressionVisitor.cs +++ b/src/Bicep.Core/Intermediate/ExpressionVisitor.cs @@ -157,7 +157,7 @@ private void VisitDescribableExpression(DescribableExpression expression) Visit(expression.Description); } - public virtual void VisitDeclaredExtensionExpression(DeclaredExtensionExpression expression) + public virtual void VisitExtensionExpression(ExtensionExpression expression) { VisitDescribableExpression(expression); Visit(expression.Config); diff --git a/src/Bicep.Core/Intermediate/IExpressionVisitor.cs b/src/Bicep.Core/Intermediate/IExpressionVisitor.cs index c108a8ace1b..51eefe2865c 100644 --- a/src/Bicep.Core/Intermediate/IExpressionVisitor.cs +++ b/src/Bicep.Core/Intermediate/IExpressionVisitor.cs @@ -61,7 +61,7 @@ public interface IExpressionVisitor void VisitDeclaredMetadataExpression(DeclaredMetadataExpression expression); - void VisitDeclaredExtensionExpression(DeclaredExtensionExpression expression); + void VisitExtensionExpression(ExtensionExpression expression); void VisitDeclaredParameterExpression(DeclaredParameterExpression expression);