File: Writers\CSharp\CSDeclarationWriter.Properties.cs
Web Access
Project: src\src\Microsoft.Cci.Extensions\Microsoft.Cci.Extensions.csproj (Microsoft.Cci.Extensions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Linq;
using Microsoft.Cci.Extensions.CSharp;
 
namespace Microsoft.Cci.Writers.CSharp
{
    public partial class CSDeclarationWriter
    {
        private void WritePropertyDefinition(IPropertyDefinition property)
        {
            IMethodDefinition accessor = null;
            IMethodDefinition getter = null;
            IMethodDefinition setter = null;
            if (property.Getter != null)
            {
                getter = property.Getter.ResolvedMethod;
                if (!_filter.Include(getter))
                    getter = null;
                accessor = getter;
            }
 
            if (property.Setter != null)
            {
                setter = property.Setter.ResolvedMethod;
                if (!_filter.Include(setter))
                    setter = null;
                if (accessor == null)
                    accessor = setter;
            }
 
            if (accessor == null)
                return;
 
            bool isIndexer = accessor.ParameterCount > (accessor == setter ? 1 : 0);
 
            if (isIndexer)
            {
                string id = property.Name.Value;
                int index = id.LastIndexOf(".");
                if (index >= 0)
                    id = id.Substring(index + 1);
 
                if (id != "Item")
                {
                    WriteFakeAttribute("System.Runtime.CompilerServices.IndexerName", "\"" + id + "\"");
                }
            }
 
            WriteAttributes(property.Attributes);
 
            // We need to preserve nullable custom attributes which are preserved by the compiler as param on the value parameter and return attributes.
            if (getter != null)
                WriteAttributes(getter.ReturnValueAttributes);
            if (setter != null)
                WriteAttributes(setter.Parameters.Last().Attributes);
 
            var writeVisibility = true;
 
            if (property.ContainingTypeDefinition.IsInterface)
            {
                writeVisibility = false;
            }
 
            if (accessor.IsExplicitInterfaceMethod())
            {
                writeVisibility = false;
            }
 
            if (writeVisibility)
            {
                WriteVisibility(property.Visibility);
            }
 
            if (property.Attributes.HasRequiredMemberAttribute())
            {
                WriteKeyword("required");
            }
 
            // Getter and Setter modifiers should be the same
            WriteMethodModifiers(accessor);
 
            if (property.GetHiddenBaseProperty(_filter) != Dummy.Property)
                WriteKeyword("new");
 
            bool getterHasIsReadOnlyAttribute = (getter?.Attributes.HasIsReadOnlyAttribute()).GetValueOrDefault();
            bool setterHasIsReadOnlyAttribute = (setter?.Attributes.HasIsReadOnlyAttribute()).GetValueOrDefault();
 
            // The readonly modifier is applied on the property itself if:
            //  * It has both a getter and a setter and both have IsReadOnlyAttribute
            //  * It only has a getter or a setter and it has IsReadOnlyAttribute
            // Otherwise, the attribute is applied directly on the getter/setter it exists for
            bool allAccessorsHaveIsReadOnlyAttribute = (getterHasIsReadOnlyAttribute && setterHasIsReadOnlyAttribute) ||
                                                       (getterHasIsReadOnlyAttribute && (setter is null)) ||
                                                       (setterHasIsReadOnlyAttribute && (getter is null));
 
            if (allAccessorsHaveIsReadOnlyAttribute && (LangVersion >= LangVersion8_0))
            {
                WriteKeyword("readonly");
            }
 
            if (property.ReturnValueIsByRef)
            {
                WriteKeyword("ref");
 
                if (property.Attributes.HasIsReadOnlyAttribute())
                    WriteKeyword("readonly");
            }
 
            WriteTypeName(property.Type, property.Attributes);
 
            if (property.IsExplicitInterfaceProperty() && _forCompilationIncludeGlobalprefix)
                Write("global::");
 
            WritePropertyName(property, accessor, accessor == setter, isIndexer);
            WriteSpace();
            WriteSymbol("{");
 
            //get
            if (getter != null)
            {
                bool isReadOnly = getterHasIsReadOnlyAttribute && !allAccessorsHaveIsReadOnlyAttribute;
                WriteAccessorDefinition(property, getter, "get", isReadOnly);
            }
            //set
            if (setter != null)
            {
                bool isReadOnly = setterHasIsReadOnlyAttribute && !allAccessorsHaveIsReadOnlyAttribute;
                WriteAccessorDefinition(property, setter, "set", isReadOnly);
            }
            WriteSpace();
            WriteSymbol("}");
        }
 
        private void WritePropertyName(IPropertyDefinition property, IMethodDefinition accessor, bool isSetterAccessor, bool isIndexer)
        {
            if (property.IsExplicitInterfaceProperty())
            {
                IMethodImplementation methodImplementation = accessor.GetMethodImplementation();
                object nullableAttributeArgument = methodImplementation.GetExplicitInterfaceMethodNullableAttributeArgument(_metadataReaderCache);
                if (nullableAttributeArgument != null)
                {
                    WriteTypeName(methodImplementation.ImplementedMethod.ContainingType, noSpace: true, nullableAttributeArgument: nullableAttributeArgument);
                    WriteSymbol(".");
                    if (isIndexer)
                    {
                        WriteIdentifier("this", false);
                        WriteIndexerParameters(accessor, isSetterAccessor);
                    }
                    else
                    {
                        string name = methodImplementation.ImplementedMethod.Name.Value;
                        WriteIdentifier(name.Substring(name.IndexOf("_") + 1));
                    }
 
                    return;
                }
            }
 
            if (isIndexer)
            {
                int index = property.Name.Value.LastIndexOf(".");
                if (index >= 0)
                    WriteIdentifier(property.Name.Value.Substring(0, index + 1) + "this", false); // +1 to include the '.'
                else
                    WriteIdentifier("this", false);
 
                WriteIndexerParameters(accessor, isSetterAccessor);
            }
            else
            {
                WriteIdentifier(property.Name);
            }
        }
 
        private void WriteIndexerParameters(IMethodDefinition accessor, bool isSetterAccessor)
        {
            var parameters = new List<IParameterDefinition>(accessor.Parameters);
            if (isSetterAccessor) // If setter remove value parameter.
                parameters.RemoveAt(parameters.Count - 1);
 
            byte? nullableContextValue = accessor.Attributes.GetCustomAttributeArgumentValue<byte?>(CSharpCciExtensions.NullableContextAttributeFullName);
            WriteParameters(parameters, accessor.ContainingType, nullableContextValue, property: true);
        }
 
        private void WriteAccessorDefinition(IPropertyDefinition property, IMethodDefinition accessor, string accessorType, bool isReadOnly)
        {
            WriteSpace();
            WriteAttributes(accessor.Attributes, writeInline: true);
            WriteAttributes(accessor.SecurityAttributes, writeInline: true);
            // If the accessor is an internal call (or a PInvoke) we should put those attributes here as well
 
            WriteMethodPseudoCustomAttributes(accessor);
 
            if (accessor.Visibility != property.Visibility)
                WriteVisibility(accessor.Visibility);
            if (isReadOnly && (LangVersion >= LangVersion8_0))
            {
                WriteKeyword("readonly");
            }
            WriteKeyword(accessorType, noSpace: true);
            WriteMethodBody(accessor);
        }
    }
}