public override void VisitObjectSyntax()

in src/Bicep.Core/TypeSystem/TypeAssignmentVisitor.cs [1317:1433]


        public override void VisitObjectSyntax(ObjectSyntax syntax)
            => AssignType(syntax, () =>
            {
                foreach (var missingDeclarationSyntax in syntax.Children.OfType<MissingDeclarationSyntax>())
                {
                    VisitMissingDeclarationSyntax(missingDeclarationSyntax);
                }

                var errors = new List<IDiagnostic>();

                var duplicatedProperties = syntax.Properties
                    .GroupByExcludingNull(prop => prop.TryGetKeyText(), LanguageConstants.IdentifierComparer)
                    .Where(group => group.Count() > 1);

                foreach (var group in duplicatedProperties)
                {
                    foreach (ObjectPropertySyntax duplicatedProperty in group)
                    {
                        errors.Add(DiagnosticBuilder.ForPosition(duplicatedProperty.Key).PropertyMultipleDeclarations(group.Key));
                    }
                }

                var childTypes = new List<TypeSymbol>();
                foreach (var child in syntax.Children)
                {
                    var childType = child switch
                    {
                        ObjectPropertySyntax x => typeManager.GetTypeInfo(x),
                        SpreadExpressionSyntax x => typeManager.GetTypeInfo(x.Expression),
                        _ => null,
                    };

                    if (childType is null)
                    {
                        continue;
                    }

                    if (child is SpreadExpressionSyntax spread &&
                        childType is not ErrorType &&
                        !TypeValidator.AreTypesAssignable(childType, LanguageConstants.Object))
                    {
                        childType = ErrorType.Create(DiagnosticBuilder.ForPosition(child).SpreadOperatorRequiresAssignableValue(spread, LanguageConstants.Object));
                    }

                    CollectErrors(errors, childType);
                    childTypes.Add(childType);
                }

                if (PropagateErrorType(errors, childTypes))
                {
                    return ErrorType.Create(errors);
                }

                // Discriminated objects should have been resolved by the declared type manager.
                var declaredType = typeManager.GetDeclaredType(syntax);

                var namedProperties = new Dictionary<string, NamedTypeProperty>(LanguageConstants.IdentifierComparer);
                var additionalProperties = new List<TypeSymbol>();
                foreach (var child in syntax.Children)
                {
                    if (child is ObjectPropertySyntax propertySyntax)
                    {
                        var resolvedType = typeManager.GetTypeInfo(propertySyntax);

                        if (propertySyntax.TryGetKeyText() is { } name)
                        {
                            if (declaredType is ObjectType objectType && objectType.Properties.TryGetValue(name, out var property))
                            {
                                // we've found a declared object type for the containing object, with a matching property name definition.
                                // preserve the type property details (name, descriptions etc.), and update the assigned type.
                                // Since this type corresponds to a value that is being supplied, make sure it has the `Required` flag and does not have the `.ReadOnly` flag
                                namedProperties[name] = new NamedTypeProperty(
                                    property.Name,
                                    resolvedType,
                                    (property.Flags | TypePropertyFlags.Required) & ~TypePropertyFlags.ReadOnly,
                                    property.Description);
                            }
                            else
                            {
                                // we've not been able to find a declared object type for the containing object, or it doesn't contain a property matching this one.
                                // best we can do is to simply generate a property for the assigned type.
                                namedProperties[name] = new NamedTypeProperty(name, resolvedType, TypePropertyFlags.Required);
                            }
                        }
                        else
                        {
                            additionalProperties.Add(resolvedType);
                        }
                    }

                    if (child is SpreadExpressionSyntax spreadSyntax)
                    {
                        var type = typeManager.GetTypeInfo(spreadSyntax.Expression);
                        if (type is ObjectType spreadType)
                        {
                            foreach (var (name, property) in spreadType.Properties)
                            {
                                namedProperties[name] = property;
                            }

                            if (spreadType.AdditionalProperties is { } spreadTypeAdditionalProperties)
                            {
                                additionalProperties.Add(spreadTypeAdditionalProperties.TypeReference.Type);
                            }
                        }
                        else
                        {
                            additionalProperties.Add(LanguageConstants.Any);
                        }
                    }
                }

                var additionalPropertiesType = additionalProperties.Any() ? TypeHelper.CreateTypeUnion(additionalProperties) : null;

                // TODO: Add structural naming?
                return new ObjectType(LanguageConstants.Object.Name, TypeSymbolValidationFlags.Default, namedProperties.Values, additionalPropertiesType is null ? null : new(additionalPropertiesType));
            });