File: Specs\Members\PropertySpec.cs
Web Access
Project: Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj (Microsoft.Extensions.Configuration.Binder.SourceGeneration)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
    public sealed record PropertySpec : MemberSpec
    {
        public PropertySpec(IPropertySymbol property, TypeRef typeRef) : base(property, typeRef)
        {
            // An override that only re-declares one accessor doesn't have a symbol for the other
            // (e.g. a get-only override of a read-write virtual property has a null SetMethod), even
            // though the base accessor is still inherited and invocable through normal member access.
            // Walk the override chain to find the accessor that will actually be used at the call site.
            IMethodSymbol? getMethod = GetEffectiveAccessor(property, getter: true);
            IMethodSymbol? setMethod = GetEffectiveAccessor(property, getter: false);
            bool setterIsPublic = setMethod?.DeclaredAccessibility is Accessibility.Public;
            bool isInitOnly = setMethod?.IsInitOnly is true;

            IsStatic = property.IsStatic;
            SetOnInit = setterIsPublic && (property.IsRequired || isInitOnly);
            CanSet = setterIsPublic && !isInitOnly;
            CanGet = getMethod?.DeclaredAccessibility is Accessibility.Public;
        }

        private static IMethodSymbol? GetEffectiveAccessor(IPropertySymbol property, bool getter)
        {
            for (IPropertySymbol? current = property; current is not null; current = current.OverriddenProperty)
            {
                IMethodSymbol? accessor = getter ? current.GetMethod : current.SetMethod;
                if (accessor is not null)
                {
                    return accessor;
                }
            }

            return null;
        }

        public ParameterSpec? MatchingCtorParam { get; set; }

        public bool IsIgnored { get; init; }

        public bool IsStatic { get; }

        public bool SetOnInit { get; }

        public override bool CanGet { get; }

        public override bool CanSet { get; }
    }
}