in src/Bicep.Core/Semantics/Namespaces/SystemNamespaceType.cs [1777:2061]
static IEnumerable<Decorator> GetBicepTemplateDecorators(IFeatureProvider featureProvider)
{
yield return new DecoratorBuilder(LanguageConstants.ParameterSecurePropertyName)
.WithDescription("Makes the parameter a secure parameter.")
.WithFlags(FunctionFlags.ParameterOutputOrTypeDecorator)
.WithAttachableType(TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Object))
.WithValidator(ValidateNotTargetingAlias)
.WithEvaluator((functionCall, decorated) =>
{
if (decorated is TypeDeclaringExpression typeDeclaringExpression)
{
return typeDeclaringExpression with { Secure = functionCall };
}
return decorated;
})
.Build();
yield return new DecoratorBuilder(LanguageConstants.ParameterAllowedPropertyName)
.WithDescription("Defines the allowed values of the parameter.")
.WithRequiredParameter("values", LanguageConstants.Array, "The allowed values.")
.WithFlags(FunctionFlags.ParameterDecorator)
.WithValidator((decoratorName, decoratorSyntax, targetType, typeManager, binder, parsingErrorLookup, diagnosticWriter) =>
{
var parentTypeSyntax = GetDeclaredTypeSyntaxOfParent(decoratorSyntax, binder);
EmitDiagnosticIfTargetingAlias(decoratorName, decoratorSyntax, parentTypeSyntax, binder, diagnosticWriter);
EmitDiagnosticIfTargetingLiteral(decoratorName, decoratorSyntax, parentTypeSyntax, typeManager, diagnosticWriter);
if (TypeValidator.AreTypesAssignable(targetType, LanguageConstants.Array) &&
SingleArgumentSelector(decoratorSyntax) is ArraySyntax allowedValues &&
allowedValues.Items.All(item => item.Value is not ArraySyntax))
{
/*
* ARM handles array params with allowed values differently. If none of items of
* the allowed values is array, it will check if the parameter value is a subset
* of the allowed values.
*/
return;
}
TypeValidator.NarrowTypeAndCollectDiagnostics(
typeManager,
binder,
parsingErrorLookup,
diagnosticWriter,
SingleArgumentSelector(decoratorSyntax),
new TypedArrayType(targetType, TypeSymbolValidationFlags.Default));
})
.WithEvaluator((functionCall, decorated) =>
{
if (decorated is DeclaredParameterExpression declaredParameterExpression &&
functionCall.Parameters.FirstOrDefault() is { } allowedValues)
{
return declaredParameterExpression with { AllowedValues = allowedValues };
}
return decorated;
})
.Build();
yield return new DecoratorBuilder(LanguageConstants.ParameterMinValuePropertyName)
.WithDescription("Defines the minimum value of the parameter.")
.WithRequiredParameter("value", LanguageConstants.Int, "The minimum value.")
.WithFlags(FunctionFlags.ParameterOutputOrTypeDecorator)
.WithAttachableType(LanguageConstants.Int)
.WithValidator(ValidateNotTargetingAlias)
.WithEvaluator((functionCall, decorated) =>
{
if (decorated is TypeDeclaringExpression typeDeclaringExpression &&
functionCall.Parameters.FirstOrDefault() is { } minValue)
{
return typeDeclaringExpression with { MinValue = minValue };
}
return decorated;
})
.Build();
yield return new DecoratorBuilder(LanguageConstants.ParameterMaxValuePropertyName)
.WithDescription("Defines the maximum value of the parameter.")
.WithRequiredParameter("value", LanguageConstants.Int, "The maximum value.")
.WithFlags(FunctionFlags.ParameterOutputOrTypeDecorator)
.WithAttachableType(LanguageConstants.Int)
.WithValidator(ValidateNotTargetingAlias)
.WithEvaluator((functionCall, decorated) =>
{
if (decorated is TypeDeclaringExpression typeDeclaringExpression &&
functionCall.Parameters.FirstOrDefault() is { } maxValue)
{
return typeDeclaringExpression with { MaxValue = maxValue };
}
return decorated;
})
.Build();
yield return new DecoratorBuilder(LanguageConstants.ParameterMinLengthPropertyName)
.WithDescription("Defines the minimum length of the parameter.")
.WithRequiredParameter("length", LanguageConstants.Int, "The minimum length.")
.WithFlags(FunctionFlags.ParameterOutputOrTypeDecorator)
.WithAttachableType(TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Array))
.WithValidator(ValidateLength)
.WithEvaluator((functionCall, decorated) =>
{
if (decorated is TypeDeclaringExpression typeDeclaringExpression &&
functionCall.Parameters.FirstOrDefault() is { } minLength)
{
return typeDeclaringExpression with { MinLength = minLength };
}
return decorated;
})
.Build();
yield return new DecoratorBuilder(LanguageConstants.ParameterMaxLengthPropertyName)
.WithDescription("Defines the maximum length of the parameter.")
.WithRequiredParameter("length", LanguageConstants.Int, "The maximum length.")
.WithFlags(FunctionFlags.ParameterOutputOrTypeDecorator)
.WithAttachableType(TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Array))
.WithValidator(ValidateLength)
.WithEvaluator((functionCall, decorated) =>
{
if (decorated is TypeDeclaringExpression typeDeclaringExpression &&
functionCall.Parameters.FirstOrDefault() is { } maxLength)
{
return typeDeclaringExpression with { MaxLength = maxLength };
}
return decorated;
})
.Build();
yield return new DecoratorBuilder(LanguageConstants.ParameterMetadataPropertyName)
.WithDescription("Defines metadata of the parameter.")
.WithRequiredParameter("object", LanguageConstants.Object, "The metadata object.")
.WithFlags(FunctionFlags.ParameterOutputOrTypeDecorator)
.WithValidator((_, decoratorSyntax, _, typeManager, binder, parsingErrorLookup, diagnosticWriter) =>
TypeValidator.NarrowTypeAndCollectDiagnostics(typeManager, binder, parsingErrorLookup, diagnosticWriter, SingleArgumentSelector(decoratorSyntax), LanguageConstants.ParameterModifierMetadata))
.WithEvaluator((functionCall, decorated) =>
{
if (decorated is TypeDeclaringExpression typeDeclaringExpression &&
functionCall.Parameters.FirstOrDefault() is { } metadata)
{
return typeDeclaringExpression with { Metadata = metadata };
}
return decorated;
})
.Build();
yield return new DecoratorBuilder(LanguageConstants.BatchSizePropertyName)
.WithDescription("Causes the resource or module for-expression to be run in sequential batches of specified size instead of the default behavior where all the resources or modules are deployed in parallel.")
.WithRequiredParameter(LanguageConstants.BatchSizePropertyName, LanguageConstants.Int, "The size of the batch")
.WithFlags(FunctionFlags.ResourceOrModuleDecorator)
// the decorator is constrained to resources and modules already - checking for array alone is simple and should be sufficient
.WithValidator((decoratorName, decoratorSyntax, targetType, typeManager, binder, _, diagnosticWriter) =>
{
if (!TypeValidator.AreTypesAssignable(targetType, LanguageConstants.Array))
{
// the resource/module declaration is not a collection
// (the compile-time constant and resource/module placement is already enforced, so we don't need a deeper type check)
diagnosticWriter.Write(DiagnosticBuilder.ForPosition(decoratorSyntax).BatchSizeNotAllowed(decoratorName));
}
const long minimumBatchSize = 1;
SyntaxBase batchSizeSyntax = SingleArgumentSelector(decoratorSyntax);
long? batchSize = TryGetIntegerLiteralValue(batchSizeSyntax);
if (batchSize is not null and < minimumBatchSize)
{
diagnosticWriter.Write(DiagnosticBuilder.ForPosition(batchSizeSyntax).BatchSizeTooSmall(batchSize.Value, minimumBatchSize));
}
})
.Build();
if (featureProvider.WaitAndRetryEnabled)
{
yield return new DecoratorBuilder(LanguageConstants.WaitUntilPropertyName)
.WithDescription("Causes the resource deployment to wait until the given condition is satisfied")
.WithRequiredParameter("predicate", OneParamLambda(LanguageConstants.Object, LanguageConstants.Bool), "The predicate applied to the resource.")
.WithRequiredParameter("maxWaitTime", LanguageConstants.String, "Maximum time used to wait until the predicate is true. Please be cautious as max wait time adds to total deployment time. It cannot be a negative value. Use [ISO 8601 duration format](https://en.wikipedia.org/wiki/ISO_8601#Durations).")
.WithFlags(FunctionFlags.ResourceDecorator)// the decorator is constrained to resources
.WithEvaluator(AddDecoratorConfigToResource)
.Build();
yield return new DecoratorBuilder(LanguageConstants.RetryOnPropertyName)
.WithDescription("Causes the resource deployment to retry when deployment failed with one of the exceptions listed")
.WithRequiredParameter("exceptionCodes", LanguageConstants.StringArray, "List of exceptions.")
.WithOptionalParameter("retryCount", TypeFactory.CreateIntegerType(minValue: 1), "Maximum number if retries on the exception.")
.WithFlags(FunctionFlags.ResourceDecorator)// the decorator is constrained to resources
.WithEvaluator(AddDecoratorConfigToResource)
.Build();
}
if (featureProvider.OnlyIfNotExistsEnabled)
{
yield return new DecoratorBuilder(LanguageConstants.OnlyIfNotExistsPropertyName)
.WithDescription("Causes the resource deployment to be skipped if the resource already exists")
.WithFlags(FunctionFlags.ResourceDecorator)// the decorator is constrained to resources
.WithEvaluator(AddDecoratorConfigToResource)
.Build();
}
yield return new DecoratorBuilder(LanguageConstants.ParameterSealedPropertyName)
.WithDescription("Marks an object parameter as only permitting properties specifically included in the type definition")
.WithFlags(FunctionFlags.ParameterOutputOrTypeDecorator)
.WithAttachableType(LanguageConstants.Object)
.WithValidator((decoratorName, decoratorSyntax, targetType, typeManager, binder, parsingErrorLookup, diagnosticWriter) =>
{
switch (UnwrapNullableSyntax(GetDeclaredTypeSyntaxOfParent(decoratorSyntax, binder)))
{
case VariableAccessSyntax variableAccess when binder.GetSymbolInfo(variableAccess) is not AmbientTypeSymbol:
diagnosticWriter.Write(DiagnosticBuilder.ForPosition(decoratorSyntax).DecoratorMayNotTargetTypeAlias(decoratorName));
break;
case AccessExpressionSyntax accessExpression when binder.GetSymbolInfo(accessExpression.BaseExpression) is not BuiltInNamespaceSymbol:
diagnosticWriter.Write(DiagnosticBuilder.ForPosition(decoratorSyntax).DecoratorMayNotTargetTypeAlias(decoratorName));
break;
case ParameterizedTypeInstantiationSyntaxBase parameterized when LanguageConstants.ResourceDerivedTypeNames.Contains(parameterized.Name.IdentifierName):
diagnosticWriter.Write(DiagnosticBuilder.ForPosition(decoratorSyntax).DecoratorMayNotTargetResourceDerivedType(decoratorName));
break;
case ObjectTypeSyntax @object when @object.AdditionalProperties is not null:
diagnosticWriter.Write(DiagnosticBuilder.ForPosition(decoratorSyntax).SealedIncompatibleWithAdditionalPropertiesDeclaration());
break;
}
})
.WithEvaluator((functionCall, decorated) =>
{
if (decorated is TypeDeclaringExpression typeDeclaringExpression)
{
return typeDeclaringExpression with { Sealed = functionCall };
}
return decorated;
})
.Build();
yield return new DecoratorBuilder(LanguageConstants.TypeDiscriminatorDecoratorName)
.WithDescription("Defines the discriminator property to use for a tagged union that is shared between all union members")
.WithRequiredParameter("value", LanguageConstants.String, "The discriminator property name.")
.WithFlags(FunctionFlags.ParameterOutputOrTypeDecorator)
.WithValidator(ValidateTypeDiscriminator)
.WithAttachableType(LanguageConstants.Object)
.Build();
yield return new DecoratorBuilder(LanguageConstants.ExportPropertyName)
.WithDescription("Allows a type, variable, or function to be imported into other Bicep files.")
.WithFlags(FunctionFlags.TypeVariableOrFunctionDecorator)
.WithEvaluator(static (functionCall, decorated) => decorated switch
{
DeclaredTypeExpression declaredType => declaredType with { Exported = functionCall },
DeclaredVariableExpression declaredVariable => declaredVariable with { Exported = functionCall },
DeclaredFunctionExpression declaredFunction => declaredFunction with { Exported = functionCall },
_ => decorated,
})
.WithValidator(static (decoratorName, decoratorSyntax, _, _, binder, _, diagnosticWriter) =>
{
var decoratorTarget = binder.GetParent(decoratorSyntax);
if (decoratorTarget is not ITopLevelNamedDeclarationSyntax)
{
diagnosticWriter.Write(DiagnosticBuilder.ForPosition(decoratorSyntax).ExportDecoratorMustTargetStatement());
}
if (decoratorTarget is not null && binder.GetSymbolInfo(decoratorTarget) is DeclaredSymbol targetedDeclaration)
{
var nonExportableSymbolsInClosure = binder.GetReferencedSymbolClosureFor(targetedDeclaration)
.Where(s => s is not VariableSymbol and
not TypeAliasSymbol and
not DeclaredFunctionSymbol and
not ImportedSymbol and
not WildcardImportSymbol and
not LocalVariableSymbol)
.Select(s => s.Name)
.Order()
.ToImmutableArray();
if (nonExportableSymbolsInClosure.Any())
{
diagnosticWriter.Write(DiagnosticBuilder.ForPosition(decoratorSyntax).ClosureContainsNonExportableSymbols(nonExportableSymbolsInClosure));
}
}
})
.Build();
}