File: Filters\NotImplementedFilter.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;
 
namespace Microsoft.Cci.Filters
{
    public class NotImplementedFilter : ICciFilter
    {
        public virtual bool Include(INamespaceDefinition ns)
        {
            return true;
        }
 
        public virtual bool Include(ICustomAttribute attribute)
        {
            return true;
        }
 
        public virtual bool Include(ITypeDefinition type)
        {
            var hasOnlyExcludedMembers = type.Members.Any() && type.Members.All(m => !Include(m));
            return !hasOnlyExcludedMembers;
        }
 
        public virtual bool Include(ITypeDefinitionMember member)
        {
            var evnt = member as IEventDefinition;
            if (evnt != null)
                return Include(evnt);
 
            var prop = member as IPropertyDefinition;
            if (prop != null)
                return Include(prop);
 
            var method = member as IMethodDefinition;
            if (method != null)
                return Include(method);
 
            return true;
        }
 
        private bool Include(IPropertyDefinition prop)
        {
            return prop.Accessors.Any(a => Include(a.ResolvedMethod));
        }
 
        private bool Include(IEventDefinition evnt)
        {
            return evnt.Accessors.Any(a => Include(a.ResolvedMethod));
        }
 
        private bool Include(IMethodDefinition method)
        {
            return IsImplemented(method);
        }
 
        private static bool IsImplemented(IMethodDefinition method)
        {
            // Generally, a method that throws NotImplementedException is considered not implemented.
            // However, if the containing assembly is marked to be a reference assembly then this is
            // likely not true as virtually all methods in reference assemblies have bodies that throw.
            //
            // Thus, if the assembly is a reference assembly, we consider it implemented.
 
            return IsContainedInReferenceAssembly(method) || !ThrowsNotImplementedException(method);
        }
 
        private static bool IsContainedInReferenceAssembly(IMethodDefinition method)
        {
            var asssembly = method.ContainingType.GetAssemblyReference().ResolvedAssembly;
            return asssembly.IsReferenceAssembly();
        }
 
        private static bool ThrowsNotImplementedException(IMethodDefinition method)
        {
            // We consider a method not implemented if it contains the following sequence of IL:
            //
            //      ...
            //      newobj instance void [<some assembly>]System.NotImplementedException::.ctor()
            //      throw
            //      ...
            //
            // Note that we deliberately ignore any IL before and after. The reason being that some methods
            // unrelated code, such as argument validation. Yes, this can result in false positives, such as
            // when methods only throw in a specific branch that doesn't apply for all scenarios. For now,
            // we just ignore this case.
            //
            // Also note that empty methods are considered implemented. There are many methods for which doing
            // nothing is a legitimate fulfillment of its contract.
            //
            // In order to be resilient to code gen differences we'll allow for any nop instructions between
            // the newobj and the throw.
 
            var lastOp = (IOperation)null;
 
            foreach (var thisOp in method.Body.Operations)
            {
                if (thisOp.OperationCode == OperationCode.Nop)
                    continue;
 
                if (lastOp != null)
                {
                    if (thisOp.OperationCode == OperationCode.Throw &&
                        lastOp.OperationCode == OperationCode.Newobj &&
                        IsNotImplementedConstructor(lastOp.Value))
                    {
                        return true;
                    }
                }
 
                lastOp = thisOp;
            }
 
            return false;
        }
 
        private static bool IsNotImplementedConstructor(object value)
        {
            var method = value as IMethodReference;
            if (method == null)
                return false;
 
            var containingType = method.ContainingType;
            var nameOptions = NameFormattingOptions.UseGenericTypeNameSuffix |
                              NameFormattingOptions.UseReflectionStyleForNestedTypeNames;
            var typeName = containingType.GetTypeName(nameOptions);
            return typeName == "System.NotImplementedException";
        }
    }
}