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

Remove experimental feature for user defined functions #13458

Merged
merged 2 commits into from
Feb 27, 2024
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
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
Loading