private DeclaredTypeAssignment? GetObjectType()

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;
        }