Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a function overload builder #1126

Merged
merged 3 commits into from
Dec 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions src/Bicep.Core/Semantics/FunctionOverload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,23 +129,5 @@ public FunctionMatchResult Match(IList<TypeSymbol> argumentTypes, out ArgumentCo

return FunctionMatchResult.Match;
}

public static FunctionOverload CreateFixed(string name, TypeSymbol returnType, params TypeSymbol[] argumentTypes) =>
new FunctionOverload(name, returnType, argumentTypes.Length, argumentTypes.Length, argumentTypes, variableParameterType: null);

public static FunctionOverload CreateFixed(string name, ReturnTypeBuilderDelegate returnTypeBuilder, params TypeSymbol[] argumentTypes) =>
new FunctionOverload(name, returnTypeBuilder, returnTypeBuilder(Enumerable.Empty<FunctionArgumentSyntax>()), argumentTypes.Length, argumentTypes.Length, argumentTypes, variableParameterType: null);

public static FunctionOverload CreatePartialFixed(string name, TypeSymbol returnType, IEnumerable<TypeSymbol> fixedArgumentTypes, TypeSymbol variableArgumentType) =>
new FunctionOverload(name, returnType, fixedArgumentTypes.Count(), null, fixedArgumentTypes, variableArgumentType);

public static FunctionOverload CreateWithVarArgs(string name, TypeSymbol returnType, int minimumArgumentCount, TypeSymbol argumentType) =>
new FunctionOverload(
name,
returnType,
minimumArgumentCount,
maximumArgumentCount: null,
Enumerable.Repeat(argumentType, minimumArgumentCount),
argumentType);
}
}
122 changes: 122 additions & 0 deletions src/Bicep.Core/Semantics/FunctionOverloadBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Immutable;
using System.Linq;
using Bicep.Core.Syntax;
using Bicep.Core.TypeSystem;

namespace Bicep.Core.Semantics
{
public sealed class FunctionOverloadBuilder
{
private readonly string name;

private string? description;

private TypeSymbol returnType;

private readonly ImmutableArray<TypeSymbol>.Builder fixedParameterTypes;

private int minimumArgumentCount;

private int? maximumArgumentCount;

private TypeSymbol? variableParameterType;

private FunctionOverload.ReturnTypeBuilderDelegate returnTypeBuilder;

private FunctionFlags flags;

public FunctionOverloadBuilder(string name)
{
this.name = name;
this.returnType = LanguageConstants.Any;
this.fixedParameterTypes = ImmutableArray.CreateBuilder<TypeSymbol>();
this.returnTypeBuilder = args => LanguageConstants.Any;
this.variableParameterType = null;
}

public FunctionOverload Build() =>
new FunctionOverload(
this.name,
this.returnTypeBuilder,
this.returnType,
this.minimumArgumentCount,
this.maximumArgumentCount,
this.fixedParameterTypes.ToImmutable(),
this.variableParameterType,
this.flags);

public FunctionOverloadBuilder WithDescription(string description)
{
this.description = description;

return this;
}
anthony-c-martin marked this conversation as resolved.
Show resolved Hide resolved

public FunctionOverloadBuilder WithReturnType(TypeSymbol returnType)
{
this.returnType = returnType;
this.returnTypeBuilder = args => returnType;

return this;
}

public FunctionOverloadBuilder WithDynamicReturnType(FunctionOverload.ReturnTypeBuilderDelegate returnTypeBuilder)
{
this.returnType = returnTypeBuilder(Enumerable.Empty<FunctionArgumentSyntax>());
this.returnTypeBuilder = returnTypeBuilder;

return this;
}

public FunctionOverloadBuilder WithFixedParameters(params TypeSymbol[] parameterTypes) =>
this.WithOptionalFixedParameters(parameterTypes.Length, parameterTypes);

public FunctionOverloadBuilder WithOptionalFixedParameters(int minimumArgumentCount, params TypeSymbol[] parameterTypes)
{
this.fixedParameterTypes.Clear();
foreach (TypeSymbol parameterType in parameterTypes)
{
this.fixedParameterTypes.Add(parameterType);
}
Comment on lines +79 to +83
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the benefit of using the builder here? The only places I can see it being used is to do a full replace - feels like it would be more straightforward to just do this.fixedParameterTypes = parameterTypes.ToImmutableArray(); similar to the way other props are being set.

Copy link
Member Author

@majastrz majastrz Dec 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly felt kind of weird to create a list and then throw it out immediately. There is also a slight memory optimization (likely not noticeable in any way) because we avoid an extra allocation from ToImmutableArray().


this.minimumArgumentCount = minimumArgumentCount;
this.maximumArgumentCount = parameterTypes.Length;

return this;
}

public FunctionOverloadBuilder WithFixedParametersAndOptionalVariableParameters(TypeSymbol variableArgumentType, params TypeSymbol[] fixedArgumentTypes)
{
this.WithFixedParameters(fixedArgumentTypes);
this.maximumArgumentCount = null;
this.variableParameterType = variableArgumentType;

return this;
}

public FunctionOverloadBuilder WithVariableParameters(int minimumArgumentCount, TypeSymbol parameterType)
{
this.fixedParameterTypes.Clear();
for (int i = 0; i < minimumArgumentCount; i++)
{
this.fixedParameterTypes.Add(parameterType);
}

this.minimumArgumentCount = minimumArgumentCount;
this.maximumArgumentCount = null;
this.variableParameterType = parameterType;

return this;
}

public FunctionOverloadBuilder WithFlags(FunctionFlags flags)
{
this.flags = flags;

return this;
}
}
}
73 changes: 53 additions & 20 deletions src/Bicep.Core/Semantics/Namespaces/AzNamespaceSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,18 @@ private static NamedObjectType GetDeploymentReturnType(ResourceScopeType targetS
// This list should be kept in-sync with ScopeHelper.CanConvertToArmJson().

var allScopes = ResourceScopeType.TenantScope | ResourceScopeType.ManagementGroupScope | ResourceScopeType.SubscriptionScope | ResourceScopeType.ResourceGroupScope;
yield return (FunctionOverload.CreateFixed("tenant", GetRestrictedTenantReturnValue), allScopes);

yield return (new FunctionOverloadBuilder("tenant").WithDynamicReturnType(GetRestrictedTenantReturnValue).WithFixedParameters().Build(), allScopes);

yield return (FunctionOverload.CreateFixed("managementGroup", GetRestrictedManagementGroupReturnValue), ResourceScopeType.ManagementGroupScope);
yield return (FunctionOverload.CreateFixed("managementGroup", GetRestrictedManagementGroupReturnValue, LanguageConstants.String), ResourceScopeType.TenantScope | ResourceScopeType.ManagementGroupScope);
yield return (new FunctionOverloadBuilder("managementGroup").WithDynamicReturnType(GetRestrictedManagementGroupReturnValue).WithFixedParameters().Build(), ResourceScopeType.ManagementGroupScope);
yield return (new FunctionOverloadBuilder("managementGroup").WithDynamicReturnType(GetRestrictedManagementGroupReturnValue).WithFixedParameters(LanguageConstants.String).Build(), ResourceScopeType.TenantScope | ResourceScopeType.ManagementGroupScope);

yield return (FunctionOverload.CreateFixed("subscription", GetSubscriptionReturnValue), ResourceScopeType.SubscriptionScope | ResourceScopeType.ResourceGroupScope);
yield return (FunctionOverload.CreateFixed("subscription", GetRestrictedSubscriptionReturnValue, LanguageConstants.String), allScopes);
yield return (new FunctionOverloadBuilder("subscription").WithDynamicReturnType(GetSubscriptionReturnValue).WithFixedParameters().Build(), ResourceScopeType.SubscriptionScope | ResourceScopeType.ResourceGroupScope);
yield return (new FunctionOverloadBuilder("subscription").WithDynamicReturnType(GetRestrictedSubscriptionReturnValue).WithFixedParameters(LanguageConstants.String).Build(), allScopes);

yield return (FunctionOverload.CreateFixed("resourceGroup", GetResourceGroupReturnValue), ResourceScopeType.ResourceGroupScope);
yield return (FunctionOverload.CreateFixed("resourceGroup", GetRestrictedResourceGroupReturnValue, LanguageConstants.String), ResourceScopeType.SubscriptionScope | ResourceScopeType.ResourceGroupScope);
yield return (FunctionOverload.CreateFixed("resourceGroup", GetRestrictedResourceGroupReturnValue, LanguageConstants.String, LanguageConstants.String), ResourceScopeType.SubscriptionScope | ResourceScopeType.ResourceGroupScope);
yield return (new FunctionOverloadBuilder("resourceGroup").WithDynamicReturnType(GetResourceGroupReturnValue).WithFixedParameters().Build(), ResourceScopeType.ResourceGroupScope);
yield return (new FunctionOverloadBuilder("resourceGroup").WithDynamicReturnType(GetRestrictedResourceGroupReturnValue).WithFixedParameters(LanguageConstants.String).Build(), ResourceScopeType.SubscriptionScope | ResourceScopeType.ResourceGroupScope);
yield return (new FunctionOverloadBuilder("resourceGroup").WithDynamicReturnType(GetRestrictedResourceGroupReturnValue).WithFixedParameters(LanguageConstants.String, LanguageConstants.String).Build(), ResourceScopeType.SubscriptionScope | ResourceScopeType.ResourceGroupScope);
}

private static IEnumerable<FunctionOverload> GetAzOverloads(ResourceScopeType resourceScope)
Expand All @@ -155,29 +156,61 @@ private static IEnumerable<FunctionOverload> GetAzOverloads(ResourceScopeType re
{
yield return functionOverload;
}

// TODO: add banned function to explain why a given function isn't available
}

// TODO: Add schema for return type
yield return FunctionOverload.CreateFixed("deployment", GetDeploymentReturnType(resourceScope));
yield return new FunctionOverloadBuilder("deployment")
.WithReturnType(GetDeploymentReturnType(resourceScope))
.WithFixedParameters()
.Build();

yield return FunctionOverload.CreateFixed("environment", GetEnvironmentReturnType());
yield return new FunctionOverloadBuilder("environment")
.WithReturnType(GetEnvironmentReturnType())
.WithFixedParameters()
.Build();

// TODO: This is based on docs. Verify
yield return FunctionOverload.CreateWithVarArgs("resourceId", LanguageConstants.String, 2, LanguageConstants.String);
yield return FunctionOverload.CreateWithVarArgs("subscriptionResourceId", LanguageConstants.String, 2, LanguageConstants.String);
yield return FunctionOverload.CreateWithVarArgs("tenantResourceId", LanguageConstants.String, 2, LanguageConstants.String);
yield return FunctionOverload.CreateWithVarArgs("extensionResourceId", LanguageConstants.String, 3, LanguageConstants.String);
yield return new FunctionOverloadBuilder("resourceId")
.WithReturnType(LanguageConstants.String)
.WithVariableParameters(2, LanguageConstants.String)
.Build();

yield return new FunctionOverloadBuilder("subscriptionResourceId")
.WithReturnType(LanguageConstants.String)
.WithVariableParameters(2, LanguageConstants.String)
.Build();

yield return new FunctionOverloadBuilder("tenantResourceId")
.WithReturnType(LanguageConstants.String)
.WithVariableParameters(2, LanguageConstants.String)
.Build();

yield return new FunctionOverloadBuilder("extensionResourceId")
.WithReturnType(LanguageConstants.String)
.WithVariableParameters(3, LanguageConstants.String)
.Build();

// TODO: Not sure about return type
yield return new FunctionOverload("providers", LanguageConstants.Array, 1, 2, Enumerable.Repeat(LanguageConstants.String, 2), null);
yield return new FunctionOverloadBuilder("providers")
.WithReturnType(LanguageConstants.Array)
.WithOptionalFixedParameters(1, LanguageConstants.String, LanguageConstants.String)
.Build();

// TODO: return type is string[]
yield return new FunctionOverload("pickZones", LanguageConstants.Array, 3, 5, new[] {LanguageConstants.String, LanguageConstants.String, LanguageConstants.String, LanguageConstants.Int, LanguageConstants.Int}, null);

// the use of FunctionPlacementConstraints.Resources prevents use of these functions anywhere where they can't be directly inlined into a resource body
yield return new FunctionOverload("reference", LanguageConstants.Object, 1, 3, Enumerable.Repeat(LanguageConstants.String, 3), null, FunctionFlags.RequiresInlining);
yield return new FunctionWildcardOverload("list*", LanguageConstants.Any, 2, 3, new[] { LanguageConstants.String, LanguageConstants.String, LanguageConstants.Object }, null, new Regex("^list[a-zA-Z]*"), FunctionFlags.RequiresInlining);
yield return new FunctionOverloadBuilder("pickZones")
.WithReturnType(LanguageConstants.Array)
.WithOptionalFixedParameters(3, LanguageConstants.String, LanguageConstants.String, LanguageConstants.String, LanguageConstants.Int, LanguageConstants.Int)
.Build();

yield return new FunctionOverloadBuilder("reference")
.WithReturnType(LanguageConstants.Object)
.WithOptionalFixedParameters(1, LanguageConstants.String, LanguageConstants.String, LanguageConstants.String)
.WithFlags(FunctionFlags.RequiresInlining)
.Build();

yield return new FunctionWildcardOverload("list*", LanguageConstants.Any, 2, 3, new[] {LanguageConstants.String, LanguageConstants.String, LanguageConstants.Object}, null, new Regex("^list[a-zA-Z]*"), FunctionFlags.RequiresInlining);
}

public AzNamespaceSymbol(ResourceScopeType resourceScope)
Expand Down
Loading