Skip to content

Commit 41892bd

Browse files
authored
Update TypeHelper.IsLiteralType to avoid catching LanguageConstants.Object (#8952)
* Update TypeHelper.IsLiteralType to avoid catching LanguageConstants.Object * Add explanatory comment to object type check in TypeHelper.IsLiteralType
1 parent e3b8f88 commit 41892bd

File tree

3 files changed

+81
-1
lines changed

3 files changed

+81
-1
lines changed

src/Bicep.Core.IntegrationTests/ScenarioTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4064,5 +4064,31 @@ param permittedSubsetArray array
40644064
result.Template.Should().HaveValueAtPath("$.parameters.permittedSubsetArray.allowedValues", new JArray("fizz", "buzz", "pop"));
40654065
result.Template.Should().NotHaveValueAtPath("$.parameters.permittedSubsetArray.items");
40664066
}
4067+
4068+
/// <summary>
4069+
/// https://github.com/Azure/bicep/issues/8950
4070+
/// </summary>
4071+
[TestMethod]
4072+
public void Test_Issue8950()
4073+
{
4074+
var result = CompilationHelper.Compile(@"
4075+
@description('App Service Plan sku')
4076+
@allowed([
4077+
{
4078+
name: 'S1'
4079+
capacity: 1
4080+
}
4081+
{
4082+
name: 'P1v3'
4083+
capacity: 1
4084+
}
4085+
])
4086+
param appServicePlanSku object
4087+
4088+
output sku string = appServicePlanSku.name
4089+
");
4090+
4091+
result.Should().NotHaveAnyDiagnostics();
4092+
}
40674093
}
40684094
}

src/Bicep.Core.IntegrationTests/UserDefinedTypeTests.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,48 @@ public void Masked_types_still_accessible_via_qualified_reference()
102102
("no-unused-params", DiagnosticLevel.Warning, "Parameter \"stringParam\" is declared but never used."),
103103
});
104104
}
105+
106+
[TestMethod]
107+
public void Allowed_decorator_may_not_be_used_on_literal_and_union_typed_parameters()
108+
{
109+
var result = CompilationHelper.Compile(ServicesWithUserDefinedTypes, @"
110+
@allowed([true])
111+
param trueParam true
112+
113+
@allowed([false])
114+
param falseParam !true
115+
116+
@allowed([1])
117+
param oneParam 1
118+
119+
@allowed([-1])
120+
param negativeOneParam -1
121+
122+
@allowed([{fizz: 'buzz'}])
123+
param fizzBuzzParam {fizz: 'buzz'}
124+
125+
@allowed(['fizz'])
126+
param fizzParam 'fizz'
127+
128+
@allowed(['fizz', 'buzz', 'pop'])
129+
param fizzBuzzPopParam 'fizz'|'buzz'|'pop'
130+
");
131+
132+
result.Should().HaveDiagnostics(new[] {
133+
("BCP295", DiagnosticLevel.Error, "The 'allowed' decorator may not be used on targets of a union or literal type. The allowed values for this parameter or type definition will be derived from the union or literal type automatically."),
134+
("no-unused-params", DiagnosticLevel.Warning, "Parameter \"trueParam\" is declared but never used."),
135+
("BCP295", DiagnosticLevel.Error, "The 'allowed' decorator may not be used on targets of a union or literal type. The allowed values for this parameter or type definition will be derived from the union or literal type automatically."),
136+
("no-unused-params", DiagnosticLevel.Warning, "Parameter \"falseParam\" is declared but never used."),
137+
("BCP295", DiagnosticLevel.Error, "The 'allowed' decorator may not be used on targets of a union or literal type. The allowed values for this parameter or type definition will be derived from the union or literal type automatically."),
138+
("no-unused-params", DiagnosticLevel.Warning, "Parameter \"oneParam\" is declared but never used."),
139+
("BCP295", DiagnosticLevel.Error, "The 'allowed' decorator may not be used on targets of a union or literal type. The allowed values for this parameter or type definition will be derived from the union or literal type automatically."),
140+
("no-unused-params", DiagnosticLevel.Warning, "Parameter \"negativeOneParam\" is declared but never used."),
141+
("BCP295", DiagnosticLevel.Error, "The 'allowed' decorator may not be used on targets of a union or literal type. The allowed values for this parameter or type definition will be derived from the union or literal type automatically."),
142+
("no-unused-params", DiagnosticLevel.Warning, "Parameter \"fizzBuzzParam\" is declared but never used."),
143+
("BCP295", DiagnosticLevel.Error, "The 'allowed' decorator may not be used on targets of a union or literal type. The allowed values for this parameter or type definition will be derived from the union or literal type automatically."),
144+
("no-unused-params", DiagnosticLevel.Warning, "Parameter \"fizzParam\" is declared but never used."),
145+
("BCP295", DiagnosticLevel.Error, "The 'allowed' decorator may not be used on targets of a union or literal type. The allowed values for this parameter or type definition will be derived from the union or literal type automatically."),
146+
("no-unused-params", DiagnosticLevel.Warning, "Parameter \"fizzBuzzPopParam\" is declared but never used."),
147+
});
148+
}
105149
}

src/Bicep.Core/TypeSystem/TypeHelper.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,17 @@ public static TypeSymbol CreateTypeUnion(params ITypeReference[] members)
8888
StringLiteralType => true,
8989
IntegerLiteralType => true,
9090
BooleanLiteralType => true,
91-
ObjectType objectType => objectType.Properties.All(kvp => IsLiteralType(kvp.Value.TypeReference.Type)),
91+
92+
// An object type can be a literal iff:
93+
// - All properties are themselves of a literal type
94+
// - No properties are optional
95+
// - Only explicitly defined properties are accepted (i.e., no additional properties are permitted)
96+
//
97+
// The lattermost condition is identified by the object type either not defining an AdditionalPropertiesType
98+
// or explicitly flagging the AdditionalPropertiesType as a fallback (the default for non-sealed user-defined types)
99+
ObjectType objectType => (objectType.AdditionalPropertiesType is null || objectType.AdditionalPropertiesFlags.HasFlag(TypePropertyFlags.FallbackProperty)) &&
100+
objectType.Properties.All(kvp => kvp.Value.Flags.HasFlag(TypePropertyFlags.Required) && IsLiteralType(kvp.Value.TypeReference.Type)),
101+
92102
// TODO for array literals when type system adds support for tuples
93103
_ => false,
94104
};

0 commit comments

Comments
 (0)