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

Add validation for parent property #7254

Merged
merged 8 commits into from
Jun 21, 2022
156 changes: 156 additions & 0 deletions src/Bicep.Core.IntegrationTests/ScenarioTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3251,5 +3251,161 @@ param userAssignedManagedIdentityName string
("BCP179", DiagnosticLevel.Warning, "Unique resource or deployment name is required when looping. The loop item variable \"roleId\" or the index variable \"index\" must be referenced in at least one of the value expressions of the following properties in the loop body: \"name\", \"scope\"")
});
}

/// <summary>
/// https://github.com/Azure/bicep/issues/7154
/// </summary>
[TestMethod]
public void Test_Issue_7154_Ternary_Syntax_Produces_Error()
{
var result = CompilationHelper.Compile(@"
var deployServerlessCosmosDb = true

resource cosmosDbServer 'Microsoft.DocumentDB/databaseAccounts@2021-07-01-preview' = {
kind: 'GlobalDocumentDB'
name: 'cosmosdbname'
location: resourceGroup().location
properties: {
createMode: 'Default'
locations: [
{
locationName: resourceGroup().location
failoverPriority: 0
}
]
databaseAccountOfferType: 'Standard'
consistencyPolicy: {
defaultConsistencyLevel: 'Session'
maxIntervalInSeconds: 5
maxStalenessPrefix: 100
}
diagnosticLogSettings: {
enableFullTextQuery: 'None'
}
}
}

resource PassDb 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2021-07-01-preview' = {
parent: cosmosDbServer
name: 'PassDb'
properties: {
resource: {
id: 'PassDb'
}
}
}

resource QPDB 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2021-07-01-preview' = if(!deployServerlessCosmosDb) {
parent: cosmosDbServer
name: 'QPDB'
properties: {
resource: {
id: 'QPDB'
}
}
}

resource container_ActorColdStorage 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2021-07-01-preview' = {
parent: deployServerlessCosmosDb? PassDb: QPDB
name: 'ActorColdStorage'
properties: {
resource: {
id: 'ActorColdStorage'
partitionKey: {
paths: [
'/Type'
]
kind: 'Hash'
}
conflictResolutionPolicy: {
mode: 'LastWriterWins'
conflictResolutionPath: '/_ts'
}
}
}
}
");
result.ExcludingLinterDiagnostics().Should().HaveDiagnostics(new[] {
("BCP239", DiagnosticLevel.Error, "Unexpected value for parent property.")
});
}

/// <summary>
/// https://github.com/Azure/bicep/issues/7154
/// </summary>
[TestMethod]
public void Test_Issue_7154_2_Ternary_Syntax_With_Parentheses_Produces_Error()
{
var result = CompilationHelper.Compile(@"
var deployServerlessCosmosDb = true

resource cosmosDbServer 'Microsoft.DocumentDB/databaseAccounts@2021-07-01-preview' = {
kind: 'GlobalDocumentDB'
name: 'cosmosdbname'
location: resourceGroup().location
properties: {
createMode: 'Default'
locations: [
{
locationName: resourceGroup().location
failoverPriority: 0
}
]
databaseAccountOfferType: 'Standard'
consistencyPolicy: {
defaultConsistencyLevel: 'Session'
maxIntervalInSeconds: 5
maxStalenessPrefix: 100
}
diagnosticLogSettings: {
enableFullTextQuery: 'None'
}
}
}

resource PassDb 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2021-07-01-preview' = {
parent: cosmosDbServer
name: 'PassDb'
properties: {
resource: {
id: 'PassDb'
}
}
}

resource QPDB 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2021-07-01-preview' = if(!deployServerlessCosmosDb) {
parent: cosmosDbServer
name: 'QPDB'
properties: {
resource: {
id: 'QPDB'
}
}
}

resource container_ActorColdStorage 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2021-07-01-preview' = {
parent: (deployServerlessCosmosDb)? PassDb: QPDB
name: 'ActorColdStorage'
properties: {
resource: {
id: 'ActorColdStorage'
partitionKey: {
paths: [
'/Type'
]
kind: 'Hash'
}
conflictResolutionPolicy: {
mode: 'LastWriterWins'
conflictResolutionPath: '/_ts'
}
}
}
}
");
result.ExcludingLinterDiagnostics().Should().HaveDiagnostics(new[] {
("BCP239", DiagnosticLevel.Error, "Unexpected value for parent property.")
});
}
}
}
5 changes: 5 additions & 0 deletions src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1406,6 +1406,11 @@ public ErrorDiagnostic UnknownModuleReferenceScheme(string badScheme, ImmutableA
TextSpan,
"BCP238",
"Unexpected new line character after a comma.");

public ErrorDiagnostic InvalidValueForParentProperty() => new(
TextSpan,
"BCP239",
"Unexpected value for parent property.");
davidcho23 marked this conversation as resolved.
Show resolved Hide resolved
}

public static DiagnosticBuilderInternal ForPosition(TextSpan span)
Expand Down
23 changes: 23 additions & 0 deletions src/Bicep.Core/Emit/EmitLimitationCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public static EmitLimitationInfo Calculate(SemanticModel model)
DetectUnexpectedResourceLoopInvariantProperties(model, diagnosticWriter);
DetectUnexpectedModuleLoopInvariantProperties(model, diagnosticWriter);
DetectUnsupportedModuleParameterAssignments(model, diagnosticWriter);
DetectInvalidValueForParentProperty(model, diagnosticWriter);

return new EmitLimitationInfo(diagnosticWriter.GetDiagnostics(), moduleScopeData, resourceScopeData);
}
Expand Down Expand Up @@ -316,6 +317,28 @@ public static void DetectUnsupportedModuleParameterAssignments(SemanticModel sem
}
}

public static void DetectInvalidValueForParentProperty(SemanticModel semanticModel, IDiagnosticWriter diagnosticWriter)
{
foreach (var resourceDeclarationSymbol in semanticModel.Root.ResourceDeclarations)
{
if (resourceDeclarationSymbol.TryGetBodyPropertyValue(LanguageConstants.ResourceParentPropertyName) is { } referenceParentSyntax)
{
/*SyntaxBase? indexExpression = null;*/
if (referenceParentSyntax is ArrayAccessSyntax arrayAccess)
davidcho23 marked this conversation as resolved.
Show resolved Hide resolved
{
referenceParentSyntax = arrayAccess.BaseExpression;
/*indexExpression = arrayAccess.IndexExpression;*/
}

// throw a diagnostic if parent syntax can not be found
if (semanticModel.ResourceMetadata.TryLookup(referenceParentSyntax) is not { })
davidcho23 marked this conversation as resolved.
Show resolved Hide resolved
{
diagnosticWriter.Write(DiagnosticBuilder.ForPosition(referenceParentSyntax.Span).InvalidValueForParentProperty());
davidcho23 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}

private static bool IsInvariant(SemanticModel semanticModel, LocalVariableSyntax itemVariable, LocalVariableSyntax? indexVariable, SyntaxBase expression)
{
var referencedLocals = LocalSymbolDependencyVisitor.GetLocalSymbolDependencies(semanticModel, expression);
Expand Down