Skip to content

Commit

Permalink
Remove experimental feature for user defined functions
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-c-martin committed Feb 27, 2024
1 parent 98d54b4 commit b1bf958
Show file tree
Hide file tree
Showing 33 changed files with 64 additions and 255 deletions.
3 changes: 0 additions & 3 deletions docs/experimental-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ Allows the ARM template layer to use a new schema to represent resources as an o
### `testFramework`
Should be enabled in tandem with `assertions` experimental feature flag for expected functionality. Allows you to author client-side, offline unit-test test blocks that reference Bicep files and mock deployment parameters in a separate `test.bicep` file using the new `test` keyword. Test blocks can be run with the command *bicep test <filepath_to_file_with_test_blocks>* which runs all `assert` statements in the Bicep files referenced by the test blocks. For more information, see [Bicep Experimental Test Framework](https://github.com/Azure/bicep/issues/11967).

### `userDefinedFunctions`
Allows you to define your own custom functions. See [User-defined functions in Bicep](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/user-defined-functions).

## Other experimental functionality

### `publish-provider` CLI Command
Expand Down
77 changes: 8 additions & 69 deletions src/Bicep.Core.IntegrationTests/CompileTimeImportTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ namespace Bicep.Core.IntegrationTests;
[TestClass]
public class CompileTimeImportTests
{
private ServiceBuilder ServicesWithUserDefinedFunctions => new ServiceBuilder()
.WithFeatureOverrides(new(TestContext, UserDefinedFunctionsEnabled: true));

[NotNull]
public TestContext? TestContext { get; set; }

Expand Down Expand Up @@ -194,7 +191,7 @@ public void Imported_variable_symbols_should_have_declarations_injected_into_com
[TestMethod]
public void Imported_function_symbols_should_have_declarations_injected_into_compiled_template()
{
var result = CompilationHelper.Compile(ServicesWithUserDefinedFunctions,
var result = CompilationHelper.Compile(
("main.bicep", """
import {greet} from 'mod.bicep'
"""),
Expand Down Expand Up @@ -234,64 +231,6 @@ public void Imported_function_symbols_should_have_declarations_injected_into_com
"""));
}

[TestMethod]
public void Importing_functions_should_be_blocked_if_user_defined_functions_feature_not_enabled()
{
var featureProviderFactory = StrictMock.Of<IFeatureProviderFactory>();
var defaultFeatures = new FeatureProvider(IConfigurationManager.GetBuiltInConfiguration());
featureProviderFactory
.Setup(m => m.GetFeatureProvider(It.Is<System.Uri>(uri => uri.AbsolutePath.EndsWith("main.bicep"))))
.Returns(defaultFeatures);
featureProviderFactory
.Setup(m => m.GetFeatureProvider(It.Is<System.Uri>(uri => uri.AbsolutePath.EndsWith("mod.bicep"))))
.Returns(new OverriddenFeatureProvider(defaultFeatures, new(UserDefinedFunctionsEnabled: true)));

var result = CompilationHelper.Compile(new ServiceBuilder().WithFeatureProviderFactory(featureProviderFactory.Object),
("main.bicep", """
import {greet} from 'mod.bicep'
"""),
("mod.bicep", """
@export()
@description('Say hi to someone')
func greet(name string) string => 'Hi, ${name}!'
"""));

result.ExcludingLinterDiagnostics().Should().HaveDiagnostics(new[]
{
("BCP343", DiagnosticLevel.Error, $@"Using a func declaration statement requires enabling EXPERIMENTAL feature ""{nameof(ExperimentalFeaturesEnabled.UserDefinedFunctions)}"".")
});
}

[TestMethod]
public void Calling_methods_on_wildcard_imports_should_be_blocked_if_user_defined_functions_feature_not_enabled()
{
var featureProviderFactory = StrictMock.Of<IFeatureProviderFactory>();
var defaultFeatures = new FeatureProvider(IConfigurationManager.GetBuiltInConfiguration());
featureProviderFactory
.Setup(m => m.GetFeatureProvider(It.Is<System.Uri>(uri => uri.AbsolutePath.EndsWith("main.bicep"))))
.Returns(defaultFeatures);
featureProviderFactory
.Setup(m => m.GetFeatureProvider(It.Is<System.Uri>(uri => uri.AbsolutePath.EndsWith("mod.bicep"))))
.Returns(new OverriddenFeatureProvider(defaultFeatures, new(UserDefinedFunctionsEnabled: true)));

var result = CompilationHelper.Compile(new ServiceBuilder().WithFeatureProviderFactory(featureProviderFactory.Object),
("main.bicep", """
import * as mod from 'mod.bicep'

output greeting string = mod.greet('friend')
"""),
("mod.bicep", """
@export()
@description('Say hi to someone')
func greet(name string) string => 'Hi, ${name}!'
"""));

result.ExcludingLinterDiagnostics().Should().HaveDiagnostics(new[]
{
("BCP343", DiagnosticLevel.Error, $@"Using a func declaration statement requires enabling EXPERIMENTAL feature ""{nameof(ExperimentalFeaturesEnabled.UserDefinedFunctions)}"".")
});
}

[TestMethod]
public void Type_symbols_imported_from_ARM_json_should_have_declarations_injected_into_compiled_template()
{
Expand Down Expand Up @@ -467,7 +406,7 @@ public void Variable_symbols_imported_from_ARM_json_with_wildcard_syntax_should_
[TestMethod]
public void Function_symbols_imported_from_ARM_json_should_have_declarations_injected_into_compiled_template()
{
var result = CompilationHelper.Compile(ServicesWithUserDefinedFunctions,
var result = CompilationHelper.Compile(
("main.bicep", """
import {greet, 'ns.cow_say' as cowSay} from 'mod.json'
"""),
Expand Down Expand Up @@ -686,7 +625,7 @@ public void Funky_ARM_allowedValues_survive_symbol_import_and_injection()
[TestMethod]
public void Symbols_imported_via_wildcard_should_have_declarations_injected_into_compiled_template()
{
var result = CompilationHelper.Compile(ServicesWithUserDefinedFunctions,
var result = CompilationHelper.Compile(
("main.bicep", """
import * as foo from 'mod.bicep'
"""),
Expand Down Expand Up @@ -1009,7 +948,7 @@ public void Imported_variable_symbols_with_a_lengthy_reference_chain_should_have
[TestMethod]
public void Imported_function_symbols_with_a_lengthy_reference_chain_should_have_declarations_injected_into_compiled_template()
{
var result = CompilationHelper.Compile(ServicesWithUserDefinedFunctions,
var result = CompilationHelper.Compile(
("main.bicep", """
import {foo} from 'mod.bicep'

Expand Down Expand Up @@ -1835,7 +1774,7 @@ public void Test_Issue12396()
[TestMethod]
public void Symbolic_name_target_is_used_when_function_import_closure_includes_a_user_defined_type()
{
var result = CompilationHelper.Compile(ServicesWithUserDefinedFunctions,
var result = CompilationHelper.Compile(
("main.bicep", """
import { capitalizer } from 'function.bicep'
"""),
Expand Down Expand Up @@ -1956,7 +1895,7 @@ public void Imported_variable_symbols_that_use_compile_time_functions_should_hav
[TestMethod]
public void User_defined_function_calls_parameters_to_other_user_defined_function_calls_are_migrated_during_import()
{
var result = CompilationHelper.Compile(ServicesWithUserDefinedFunctions,
var result = CompilationHelper.Compile(
("main.bicep", """
import {getSubnetNumber, isWindows} from 'types.bicep'

Expand Down Expand Up @@ -2042,7 +1981,7 @@ public void Imported_objects_can_be_used_in_discriminated_object_type_narrowing(
[TestMethod]
public void LanguageVersion_2_should_be_used_if_types_imported_via_wildcard()
{
var result = CompilationHelper.Compile(ServicesWithUserDefinedFunctions,
var result = CompilationHelper.Compile(
("main.bicep", """
import * as types from 'types.bicep'
"""),
Expand Down Expand Up @@ -2080,7 +2019,7 @@ INVALID FILE
[TestMethod]
public void LanguageVersion_2_should_be_used_if_types_imported_via_closure()
{
var result = CompilationHelper.Compile(ServicesWithUserDefinedFunctions,
var result = CompilationHelper.Compile(
("main.bicep", """
import {a} from 'shared.bicep'
"""),
Expand Down
3 changes: 1 addition & 2 deletions src/Bicep.Core.IntegrationTests/EvaluationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1134,7 +1134,6 @@ public void Type_syntax_is_evaluated_correctly()
[TestMethod]
public void Functions_are_evaluated_correctly()
{
var services = new ServiceBuilder().WithFeatureOverrides(new(TestContext, UserDefinedFunctionsEnabled: true));
var bicepFile = @"
// yeah, this is a bit pointless
func joinWithSpace(values string[]) string => join(values, ' ')
Expand All @@ -1152,7 +1151,7 @@ func joinWithSpace(values string[]) string => join(values, ' ')
})
";

var (template, _, _) = CompilationHelper.Compile(services, bicepFile);
var (template, _, _) = CompilationHelper.Compile(bicepFile);

using (new AssertionScope())
{
Expand Down
3 changes: 1 addition & 2 deletions src/Bicep.Core.IntegrationTests/LambdaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,7 @@ public void DeployTimeConstant_detection_works_with_lambdas()
[TestMethod]
public void Function_recursion_is_blocked()
{
var services = new ServiceBuilder().WithFeatureOverrides(BicepTestConstants.FeatureOverrides with { UserDefinedFunctionsEnabled = true });
var result = CompilationHelper.Compile(services, @"
var result = CompilationHelper.Compile(@"
func recursive() string => recursive()
func recursiveA() string => recursiveB()
Expand Down
25 changes: 7 additions & 18 deletions src/Bicep.Core.IntegrationTests/ScenarioTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4798,9 +4798,7 @@ public void Lambda_variable_declarations_should_overwrite_globally_scoped_functi
[TestMethod]
public void Typed_lambda_variable_declarations_should_overwrite_globally_scoped_functions()
{
var services = new ServiceBuilder().WithFeatureOverrides(new(UserDefinedFunctionsEnabled: true));

var result = CompilationHelper.Compile(services, @"
var result = CompilationHelper.Compile(@"
func foo(resourceGroup string) string => resourceGroup('test')
");

Expand Down Expand Up @@ -5424,7 +5422,6 @@ public void Test_Issue12912()
{

var result = CompilationHelper.Compile(
Services.WithFeatureOverrides(new(UserDefinedFunctionsEnabled: true)),
("main.bicep", """
func test() object => loadJsonContent('./repro-data.json')
func test2() string => loadTextContent('./repro-data.json')
Expand All @@ -5449,7 +5446,6 @@ public void Test_Issue12698()
{

var result = CompilationHelper.Compile(
Services.WithFeatureOverrides(new(UserDefinedFunctionsEnabled: true)),
("main.bicep", """
import { MyFunction } from 'export.bicep'

Expand Down Expand Up @@ -5492,17 +5488,17 @@ public void Test_Issue12799()
output bothTrue bool = one && two
"""),
("bicepconfig.bicep", """
var json = loadJsonContent('bicepconfig.json')
var json = loadJsonContent('foo.json')
func testFunction(b bool) bool => b
@export()
var directExport = json.experimentalFeaturesEnabled.userDefinedFunctions
var directExport = json.bar.baz
@export()
var functionExport = testFunction(json.experimentalFeaturesEnabled.userDefinedFunctions)
var functionExport = testFunction(json.bar.baz)
"""),
("bicepconfig.json", """
("foo.json", """
{
"experimentalFeaturesEnabled": {
"userDefinedFunctions": true
"bar": {
"baz": true
}
}
"""));
Expand Down Expand Up @@ -5650,13 +5646,6 @@ func greetMultiple(names string[]) string[] => map(names, name => greet(name))
("main.bicep", """
param foo string
param foo2 string[]
"""),
("bicepconfig.json", """
{
"experimentalFeaturesEnabled": {
"userDefinedFunctions": true
}
}
"""));

result.Should().NotHaveAnyDiagnostics();
Expand Down
31 changes: 6 additions & 25 deletions src/Bicep.Core.IntegrationTests/UserDefinedFunctionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,12 @@ namespace Bicep.Core.IntegrationTests;
[TestClass]
public class UserDefinedFunctionTests
{
private ServiceBuilder Services => new ServiceBuilder()
.WithFeatureOverrides(new(TestContext, UserDefinedFunctionsEnabled: true));

[NotNull] public TestContext? TestContext { get; set; }

[TestMethod]
public void User_defined_functions_basic_case()
{
var result = CompilationHelper.Compile(Services, @"
var result = CompilationHelper.Compile(@"
func buildUrl(https bool, hostname string, path string) string => '${https ? 'https' : 'http'}:https://${hostname}${empty(path) ? '' : '/${path}'}'
output foo string = buildUrl(true, 'google.com', 'search')
Expand All @@ -35,7 +32,7 @@ public void User_defined_functions_basic_case()
[TestMethod]
public void Outer_scope_symbolic_references_are_blocked()
{
var result = CompilationHelper.Compile(Services, @"
var result = CompilationHelper.Compile(@"
param foo string
var bar = 'abc'
func getBaz() string => 'baz'
Expand All @@ -52,7 +49,7 @@ public void Outer_scope_symbolic_references_are_blocked()
[TestMethod]
public void Functions_can_have_descriptions_applied()
{
var result = CompilationHelper.Compile(Services, @"
var result = CompilationHelper.Compile(@"
@description('Returns foo')
func returnFoo() string => 'foo'
Expand All @@ -68,15 +65,13 @@ public void Functions_can_have_descriptions_applied()
[TestMethod]
public void User_defined_functions_support_custom_types()
{
var services = new ServiceBuilder().WithFeatureOverrides(new(UserDefinedFunctionsEnabled: true));

var result = CompilationHelper.Compile(services, @"
var result = CompilationHelper.Compile(@"
func getAOrB(aOrB ('a' | 'b')) bool => (aOrB == 'a')
");

result.Should().NotHaveAnyDiagnostics();

result = CompilationHelper.Compile(services, @"
result = CompilationHelper.Compile(@"
func getAOrB(aOrB bool) ('a' | 'b') => aOrB ? 'a' : 'b'
");

Expand All @@ -86,26 +81,12 @@ func getAOrB(aOrB ('a' | 'b')) bool => (aOrB == 'a')
[TestMethod]
public void User_defined_functions_unsupported_runtime_functions()
{
var result = CompilationHelper.Compile(Services, @"
var result = CompilationHelper.Compile(@"
func useRuntimeFunction() string => reference('foo').bar
");

result.Should().HaveDiagnostics(new[] {
("BCP341", DiagnosticLevel.Error, "This expression is being used inside a function declaration, which requires a value that can be calculated at the start of the deployment."),
});
}

[TestMethod]
public void User_defined_functions_requires_experimental_feature_enabled()
{
var services = new ServiceBuilder().WithFeatureOverrides(new());

var result = CompilationHelper.Compile(services, @"
func useRuntimeFunction() string => 'test'
");

result.Should().HaveDiagnostics(new[] {
("BCP343", DiagnosticLevel.Error, "Using a func declaration statement requires enabling EXPERIMENTAL feature \"UserDefinedFunctions\"."),
});
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
[
{
"label": "func",
"kind": "snippet",
"detail": "Function declaration",
"documentation": {
"kind": "markdown",
"value": "```bicep\nfunc name() outputType => \n``` \n"
},
"deprecated": false,
"preselect": false,
"sortText": "2_func",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "func ${1:name}() ${2:outputType} => $0"
}
},
{
"label": "import",
"kind": "keyword",
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
var json = loadJsonContent('bicepconfig.json')
var json = loadJsonContent('foo.json')
func testFunction(b bool) bool => b
@export()
var directExport = json.experimentalFeaturesEnabled.userDefinedFunctions
var directExport = json.bar.baz
@export()
var functionExport = testFunction(json.experimentalFeaturesEnabled.userDefinedFunctions)
var functionExport = testFunction(json.bar.baz)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"bar": {
"baz": true
}
}
Loading

0 comments on commit b1bf958

Please sign in to comment.