in src/Bicep.Core/TypeSystem/DeclaredTypeManager.cs [1728:1885]
private DeclaredTypeAssignment? GetObjectType(ObjectSyntax syntax)
{
var parent = GetClosestMaybeTypedAncestor(syntax);
switch (parent)
{
case ResourceDeclarationSyntax:
if (GetDeclaredTypeAssignment(parent)?.Reference.Type is not ResourceType resourceType)
{
return null;
}
// the object literal's parent is a resource declaration, which makes this the body of the resource
// the declared type will be the same as the parent
return TryCreateAssignment(ResolveDiscriminatedObjects(resourceType.Body.Type, syntax), syntax);
case ModuleDeclarationSyntax:
if (GetDeclaredTypeAssignment(parent)?.Reference.Type is not ModuleType moduleType)
{
return null;
}
// the object literal's parent is a module declaration, which makes this the body of the module
// the declared type will be the same as the parent
return TryCreateAssignment(ResolveDiscriminatedObjects(moduleType.Body.Type, syntax), syntax);
case IfConditionSyntax:
if (GetDeclaredTypeAssignment(parent) is not { } ifParentTypeAssignment)
{
return null;
}
// if-condition declared type already resolved discriminators and used the object as the declaring syntax
Debug.Assert(ReferenceEquals(syntax, ifParentTypeAssignment.DeclaringSyntax), "ReferenceEquals(syntax,parentTypeAssignment.DeclaringSyntax)");
// the declared type will be the same as the parent
return ifParentTypeAssignment;
case ForSyntax:
if (GetDeclaredTypeAssignment(parent) is not { } forParentTypeAssignment ||
forParentTypeAssignment.Reference.Type is not ArrayType arrayType)
{
return null;
}
// the parent is a for-expression
// this object is the body of the array, so its declared type is the type of the item
// (discriminators have already been resolved when declared type was determined for the for-expression
return TryCreateAssignment(arrayType.Item.Type, syntax, forParentTypeAssignment.Flags);
case ObjectPropertySyntax:
if (GetNonNullableTypeAssignment(parent) is not { } objectPropertyAssignment ||
objectPropertyAssignment.Reference.Type is not { } objectPropertyParent)
{
return null;
}
// the object is the value of a property of another object
// use the declared type of the property and propagate the flags
return TryCreateAssignment(ResolveDiscriminatedObjects(objectPropertyParent, syntax), syntax, objectPropertyAssignment.Flags);
case ArrayItemSyntax:
if (GetNonNullableTypeAssignment(parent) is not { } arrayItemAssignment ||
arrayItemAssignment.Reference.Type is not { } arrayParent)
{
return null;
}
// the object is an item in an array
// use the item's type and propagate flags
return TryCreateAssignment(ResolveDiscriminatedObjects(arrayParent, syntax), syntax, arrayItemAssignment.Flags);
case ExtensionWithClauseSyntax:
parent = this.binder.GetParent(parent);
if (parent is null)
{
throw new InvalidOperationException("Expected ImportWithClauseSyntax to have a parent.");
}
ObjectType? configType = null;
if (GetDeclaredTypeAssignment(parent) is not { } extensionAssignment)
{
return null;
}
if (extensionAssignment.Reference.Type is NamespaceType namespaceType)
{
// This case is extension declarations in bicep files.
configType = namespaceType.ConfigurationType;
}
else if (parent is ExtensionConfigAssignmentSyntax && extensionAssignment.Reference.Type is ObjectType configTypeFromAssignment)
{
// This case is extension config assignments in bicepparam files.
configType = configTypeFromAssignment;
}
if (configType is null)
{
// this namespace doesn't support configuration, but it has been provided.
// we'll check for this during type assignment.
return null;
}
// the object is an item in an array
// use the item's type and propagate flags
return TryCreateAssignment(ResolveDiscriminatedObjects(configType.Type, syntax), syntax, extensionAssignment.Flags);
case FunctionArgumentSyntax:
case OutputDeclarationSyntax parentOutput when syntax == parentOutput.Value:
case VariableDeclarationSyntax parentVariable when syntax == parentVariable.Value:
if (GetNonNullableTypeAssignment(parent) is not { } parentAssignment)
{
return null;
}
return TryCreateAssignment(ResolveDiscriminatedObjects(parentAssignment.Reference.Type, syntax), syntax, parentAssignment.Flags);
case ParameterDefaultValueSyntax:
// if we're in a parameter default value, get the declared type of the parameter itself
parent = this.binder.GetParent(parent);
if (parent is null)
{
throw new InvalidOperationException("Expected ParameterDefaultValueSyntax to have a parent.");
}
if (GetDeclaredTypeAssignment(parent) is not { } parameterAssignment)
{
return null;
}
return TryCreateAssignment(ResolveDiscriminatedObjects(parameterAssignment.Reference.Type, syntax), syntax, parameterAssignment.Flags);
case ParameterAssignmentSyntax:
if (GetDeclaredTypeAssignment(parent) is not { } parameterAssignmentTypeAssignment)
{
return null;
};
return TryCreateAssignment(parameterAssignmentTypeAssignment.Reference.Type, syntax);
case SpreadExpressionSyntax when GetClosestMaybeTypedAncestor(parent) is { } grandParent &&
GetDeclaredTypeAssignment(grandParent)?.Reference is ObjectType enclosingObjectType:
var type = TypeHelper.MakeRequiredPropertiesOptional(enclosingObjectType);
return TryCreateAssignment(type, syntax);
case TypedLambdaSyntax lambda when binder.IsEqualOrDescendent(syntax, lambda.Body):
if (GetTypedLambdaType(lambda).Reference is not LambdaType lambdaType)
{
return null;
}
return TryCreateAssignment(lambdaType.ReturnType, syntax);
}
return null;
}