Esempio n. 1
0
        static CodeTypeDeclaration CreateDelegateDeclaration(TypeDefinition publicType, AttributeFilter attributeFilter)
        {
            var invokeMethod = publicType.Methods.Single(m => m.Name == "Invoke");

            using (NullableContext.Push(invokeMethod)) // for delegates NullableContextAttribute is stored on Invoke method
            {
                var name  = publicType.Name;
                var index = name.IndexOf('`');
                if (index != -1)
                {
                    name = name.Substring(0, index);
                }
                var declaration = new CodeTypeDelegate(name)
                {
                    Attributes       = MemberAttributes.Public,
                    CustomAttributes = CreateCustomAttributes(publicType, attributeFilter),
                    ReturnType       = invokeMethod.ReturnType.CreateCodeTypeReference(invokeMethod.MethodReturnType),
                };

                // CodeDOM. No support. Return type attributes.
                PopulateCustomAttributes(invokeMethod.MethodReturnType, declaration.CustomAttributes, type => type.MakeReturn(), attributeFilter);
                PopulateGenericParameters(publicType, declaration.TypeParameters, attributeFilter, _ => true);
                PopulateMethodParameters(invokeMethod, declaration.Parameters, attributeFilter);

                // Of course, CodeDOM doesn't support generic type parameters for delegates. Of course.
                if (declaration.TypeParameters.Count > 0)
                {
                    var parameterNames = from parameterType in declaration.TypeParameters.Cast <CodeTypeParameter>()
                                         select parameterType.Name;
                    declaration.Name = string.Format(CultureInfo.InvariantCulture, "{0}<{1}>", declaration.Name, string.Join(", ", parameterNames));
                }

                return(declaration);
            }
        }
Esempio n. 2
0
 static void AddMemberToTypeDeclaration(CodeTypeDeclaration typeDeclaration,
                                        IMemberDefinition memberInfo,
                                        AttributeFilter attributeFilter)
 {
     using (NullableContext.Push(memberInfo))
     {
         if (memberInfo is MethodDefinition methodDefinition)
         {
             if (methodDefinition.IsConstructor)
             {
                 AddCtorToTypeDeclaration(typeDeclaration, methodDefinition, attributeFilter);
             }
             else
             {
                 AddMethodToTypeDeclaration(typeDeclaration, methodDefinition, attributeFilter);
             }
         }
         else if (memberInfo is PropertyDefinition propertyDefinition)
         {
             AddPropertyToTypeDeclaration(typeDeclaration, propertyDefinition, attributeFilter);
         }
         else if (memberInfo is EventDefinition eventDefinition)
         {
             AddEventToTypeDeclaration(typeDeclaration, eventDefinition, attributeFilter);
         }
         else if (memberInfo is FieldDefinition fieldDefinition)
         {
             AddFieldToTypeDeclaration(typeDeclaration, fieldDefinition, attributeFilter);
         }
     }
 }
Esempio n. 3
0
        // TODO: Assembly references?
        // TODO: Better handle namespaces - using statements? - requires non-qualified type names
        static string CreatePublicApiForAssembly(AssemblyDefinition assembly, Func <TypeDefinition, bool> shouldIncludeType, bool shouldIncludeAssemblyAttributes, string[] whitelistedNamespacePrefixes, AttributeFilter attributeFilter)
        {
            using (var provider = new CSharpCodeProvider())
            {
                var compileUnit = new CodeCompileUnit();
                if (shouldIncludeAssemblyAttributes && assembly.HasCustomAttributes)
                {
                    PopulateCustomAttributes(assembly, compileUnit.AssemblyCustomAttributes, attributeFilter);
                }

                var publicTypes = assembly.Modules.SelectMany(m => m.GetTypes())
                                  .Where(t => !t.IsNested && ShouldIncludeType(t) && shouldIncludeType(t))
                                  .OrderBy(t => t.FullName, StringComparer.Ordinal);
                foreach (var publicType in publicTypes)
                {
                    var @namespace = compileUnit.Namespaces.Cast <CodeNamespace>()
                                     .FirstOrDefault(n => n.Name == publicType.Namespace);
                    if (@namespace == null)
                    {
                        @namespace = new CodeNamespace(publicType.Namespace);
                        compileUnit.Namespaces.Add(@namespace);
                    }

                    using (NullableContext.Push(publicType))
                    {
                        var typeDeclaration = CreateTypeDeclaration(publicType, whitelistedNamespacePrefixes, attributeFilter);
                        @namespace.Types.Add(typeDeclaration);
                    }
                }

                using (var writer = new StringWriter())
                {
                    var cgo = new CodeGeneratorOptions
                    {
                        BracingStyle             = "C",
                        BlankLinesBetweenMembers = false,
                        VerbatimOrder            = false,
                        IndentString             = "    "
                    };

                    provider.GenerateCodeFromCompileUnit(compileUnit, writer, cgo);
                    return(CodeNormalizer.NormalizeGeneratedCode(writer));
                }
            }
        }
Esempio n. 4
0
        static void PopulateGenericParameters(IGenericParameterProvider publicType, CodeTypeParameterCollection parameters, AttributeFilter attributeFilter, Func <GenericParameter, bool> shouldUseParameter)
        {
            foreach (var parameter in publicType.GenericParameters.Where(shouldUseParameter))
            {
                // A little hacky. Means we get "in" and "out" prefixed on any constraints, but it's either that
                // or add it as a custom attribute
                var name = parameter.Name;
                if (parameter.IsCovariant)
                {
                    name = "out " + name;
                }
                if (parameter.IsContravariant)
                {
                    name = "in " + name;
                }

                var attributeCollection = new CodeAttributeDeclarationCollection();
                if (parameter.HasCustomAttributes)
                {
                    PopulateCustomAttributes(parameter, attributeCollection, attributeFilter);
                }

                var typeParameter = new CodeTypeParameter(name)
                {
                    HasConstructorConstraint =
                        parameter.HasDefaultConstructorConstraint && !parameter.HasNotNullableValueTypeConstraint
                };

                typeParameter.CustomAttributes.AddRange(attributeCollection.OfType <CodeAttributeDeclaration>().ToArray());

                var nullableConstraint  = parameter.GetNullabilityMap().First();
                var unmanagedConstraint = parameter.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.Runtime.CompilerServices.IsUnmanagedAttribute");

                if (parameter.HasNotNullableValueTypeConstraint)
                {
                    typeParameter.Constraints.Add(unmanagedConstraint ? " unmanaged" : " struct");
                }

                if (parameter.HasReferenceTypeConstraint)
                {
                    typeParameter.Constraints.Add(nullableConstraint == true ? " class?" : " class");
                }
                else if (nullableConstraint == false)
                {
                    typeParameter.Constraints.Add(" notnull");
                }

                using (NullableContext.Push(parameter))
                {
                    foreach (var constraint in parameter.Constraints.Where(constraint => !IsSpecialConstraint(constraint)))
                    {
                        // for generic constraints like IEnumerable<T> call to GetElementType() returns TypeReference with Name = !0
                        var typeReference = constraint.ConstraintType /*.GetElementType()*/.CreateCodeTypeReference(constraint);
                        typeParameter.Constraints.Add(typeReference);
                    }
                }
                parameters.Add(typeParameter);
            }

            bool IsSpecialConstraint(GenericParameterConstraint constraint)
            {
                // struct
                if (constraint.ConstraintType is TypeReference reference && reference.FullName == "System.ValueType")
                {
                    return(true);
                }

                // unmanaged
                if (constraint.ConstraintType.IsUnmanaged())
                {
                    return(true);
                }

                return(false);
            }
        }
Esempio n. 5
0
        static CodeTypeDeclaration CreateTypeDeclaration(TypeDefinition publicType, string[] whitelistedNamespacePrefixes, AttributeFilter attributeFilter)
        {
            if (publicType.IsDelegate())
            {
                return(CreateDelegateDeclaration(publicType, attributeFilter));
            }

            var            @static    = false;
            TypeAttributes attributes = 0;

            if (publicType.IsPublic || publicType.IsNestedPublic)
            {
                attributes |= TypeAttributes.Public;
            }
            if (publicType.IsNestedFamily || publicType.IsNestedFamilyOrAssembly)
            {
                attributes |= TypeAttributes.NestedFamily;
            }
            if (publicType.IsSealed && !publicType.IsAbstract)
            {
                attributes |= TypeAttributes.Sealed;
            }
            else if (!publicType.IsSealed && publicType.IsAbstract && !publicType.IsInterface)
            {
                attributes |= TypeAttributes.Abstract;
            }
            else if (publicType.IsSealed && publicType.IsAbstract)
            {
                @static = true;
            }

            // Static support is a hack. CodeDOM does support it, and this isn't
            // correct C#, but it's good enough for our API outline
            var name = publicType.Name;

            var isStruct = publicType.IsValueType && !publicType.IsPrimitive && !publicType.IsEnum;

            var @readonly = isStruct && publicType.CustomAttributes.Any(a =>
                                                                        a.AttributeType.FullName == "System.Runtime.CompilerServices.IsReadOnlyAttribute");

            var index = name.IndexOf('`');

            if (index != -1)
            {
                name = name.Substring(0, index);
            }

            var declarationName = string.Empty;

            if (@readonly)
            {
                declarationName += CodeNormalizer.ReadonlyMarker;
            }
            if (@static)
            {
                declarationName += CodeNormalizer.StaticMarker;
            }

            declarationName += name;

            var declaration = new CodeTypeDeclaration(declarationName)
            {
                CustomAttributes = CreateCustomAttributes(publicType, attributeFilter),
                // TypeAttributes must be specified before the IsXXX as they manipulate TypeAttributes!
                TypeAttributes = attributes,
                IsClass        = publicType.IsClass,
                IsEnum         = publicType.IsEnum,
                IsInterface    = publicType.IsInterface,
                IsStruct       = isStruct,
            };

            if (declaration.IsInterface && publicType.BaseType != null)
            {
                throw new NotImplementedException("Base types for interfaces needs testing");
            }

            PopulateGenericParameters(publicType, declaration.TypeParameters, attributeFilter, parameter =>
            {
                var declaringType = publicType.DeclaringType;

                while (declaringType != null)
                {
                    if (declaringType.GenericParameters.Any(p => p.Name == parameter.Name))
                    {
                        return(false); // https://github.com/ApiApprover/ApiApprover/issues/108
                    }
                    declaringType = declaringType.DeclaringType;
                }

                return(true);
            });

            if (publicType.BaseType != null && ShouldOutputBaseType(publicType))
            {
                if (publicType.BaseType.FullName == "System.Enum")
                {
                    var underlyingType = publicType.GetEnumUnderlyingType();
                    if (underlyingType.FullName != "System.Int32")
                    {
                        declaration.BaseTypes.Add(underlyingType.CreateCodeTypeReference());
                    }
                }
                else
                {
                    declaration.BaseTypes.Add(publicType.BaseType.CreateCodeTypeReference(publicType));
                }
            }
            foreach (var @interface in publicType.Interfaces.OrderBy(i => i.InterfaceType.FullName, StringComparer.Ordinal)
                     .Select(t => new { Reference = t, Definition = t.InterfaceType.Resolve() })
                     .Where(t => ShouldIncludeType(t.Definition))
                     .Select(t => t.Reference))
            {
                declaration.BaseTypes.Add(@interface.InterfaceType.CreateCodeTypeReference(@interface));
            }

            foreach (var memberInfo in publicType.GetMembers().Where(memberDefinition => ShouldIncludeMember(memberDefinition, whitelistedNamespacePrefixes)).OrderBy(m => m.Name, StringComparer.Ordinal))
            {
                AddMemberToTypeDeclaration(declaration, memberInfo, attributeFilter);
            }

            // Fields should be in defined order for an enum
            var fields = !publicType.IsEnum
                ? publicType.Fields.OrderBy(f => f.Name, StringComparer.Ordinal)
                : (IEnumerable <FieldDefinition>)publicType.Fields;

            foreach (var field in fields)
            {
                AddMemberToTypeDeclaration(declaration, field, attributeFilter);
            }

            foreach (var nestedType in publicType.NestedTypes.Where(ShouldIncludeType).OrderBy(t => t.FullName, StringComparer.Ordinal))
            {
                using (NullableContext.Push(nestedType))
                {
                    var nestedTypeDeclaration = CreateTypeDeclaration(nestedType, whitelistedNamespacePrefixes, attributeFilter);
                    declaration.Members.Add(nestedTypeDeclaration);
                }
            }

            return(declaration);
        }