File: Filters\InternalsAndPublicCciFilter.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.Linq;
using Microsoft.Cci.Extensions;
using Microsoft.Cci.Extensions.CSharp;
 
namespace Microsoft.Cci.Filters
{
    /// <summary>
    /// An <see cref="ICciFilter"/> to include <c>internal</c> and <c>public</c> members.
    /// </summary>
    /// <remarks>
    /// This is a variant of <see cref="PublicOnlyCciFilter"/>. This <see cref="ICciFilter"/> has the following
    /// differences:
    /// <list type="number">
    /// <item>Includes <c>internal</c> members.</item>
    /// <item>Reorders a few checks.</item>
    /// </list>
    /// </remarks>
    public class InternalsAndPublicCciFilter : ICciFilter
    {
        public InternalsAndPublicCciFilter(bool excludeAttributes = true)
            : this(excludeAttributes, includeForwardedTypes: false)
        {
        }
 
        public InternalsAndPublicCciFilter(bool excludeAttributes, bool includeForwardedTypes)
        {
            ExcludeAttributes = excludeAttributes;
            IncludeForwardedTypes = includeForwardedTypes;
        }
 
        public bool IncludeForwardedTypes { get; set; }
 
        public bool ExcludeAttributes { get; set; }
 
        public virtual bool Include(INamespaceDefinition ns)
        {
            // Only include non-empty namespaces.
            return ns.GetTypes(IncludeForwardedTypes).Any(Include);
        }
 
        public virtual bool Include(ITypeDefinition type)
        {
            if (Dummy.Type == type)
            {
                return false;
            }
 
            return TypeHelper.IsVisibleToFriendAssemblies(type);
        }
 
        public virtual bool Include(ITypeDefinitionMember member)
        {
            // Include internal and private protected members.
            if (member.Visibility == TypeMemberVisibility.Family ||
                member.Visibility == TypeMemberVisibility.FamilyAndAssembly)
            {
                // Similar to special case in PublicOnlyCciFilter, include protected members even of a sealed type.
                // This is necessary to generate compilable code e.g. callers with IVT dependencies on this assembly
                // may call internal methods in a sealed type. (IsVisibleToFriendAssemblies() includes the IsSealed
                // check for other use cases besides this one.)
                return true;
            }
 
            // Include public(-ish) members and explicit interface implementations.
            if (member.IsVisibleToFriendAssemblies())
            {
                return true;
            }
 
            // If a type is abstract and has an internal or public constructor, it must expose all abstract members.
            var containingType = member.ContainingTypeDefinition;
            if (containingType.IsAbstract &&
                member.IsAbstract() &&
                containingType.IsConstructorVisibleToFriendAssemblies())
            {
                return true;
            }
 
            // Otherwise...
            return false;
        }
 
        public virtual bool Include(ICustomAttribute attribute)
        {
            if (ExcludeAttributes)
            {
                return false;
            }
 
            // Exclude attributes not visible outside the assembly.
            var attributeDef = attribute.Type.GetDefinitionOrNull();
            if (attributeDef != null && !TypeHelper.IsVisibleToFriendAssemblies(attributeDef))
            {
                return false;
            }
 
            // Exclude attributes with typeof() argument of a type invisible to friend assemblies.
            foreach (var arg in attribute.Arguments.OfType<IMetadataTypeOf>())
            {
                var typeDef = arg.TypeToGet.GetDefinitionOrNull();
                if (typeDef != null && !TypeHelper.IsVisibleToFriendAssemblies(typeDef))
                {
                    return false;
                }
            }
 
            // Otherwise...
            return true;
        }
    }
}