diff --git a/src/PublicApiGenerator/ApiGenerator.cs b/src/PublicApiGenerator/ApiGenerator.cs index b425fc6..71856ea 100644 --- a/src/PublicApiGenerator/ApiGenerator.cs +++ b/src/PublicApiGenerator/ApiGenerator.cs @@ -626,6 +626,9 @@ static void AddMethodToTypeDeclaration(CodeTypeDeclaration typeDeclaration, Meth var returnType = member.ReturnType.CreateCodeTypeReference(member.MethodReturnType); + if (IsUnsafeSignatureType(member.ReturnType) || member.Parameters.Any(p => IsUnsafeSignatureType(p.ParameterType))) + returnType = MakeUnsafe(returnType); + var method = new CodeMemberMethod { Name = memberName, @@ -640,6 +643,22 @@ static void AddMethodToTypeDeclaration(CodeTypeDeclaration typeDeclaration, Meth typeDeclaration.Members.Add(method); } + static bool IsUnsafeSignatureType(TypeReference typeReference) + { + while (true) + { + if (typeReference.IsPointer) return true; + + if (typeReference.IsArray || typeReference.IsByReference) + { + typeReference = typeReference.GetElementType(); + continue; + } + + return false; + } + } + static bool IsExtensionMethod(ICustomAttributeProvider method) { return method.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute"); @@ -889,6 +908,9 @@ static void AddFieldToTypeDeclaration(CodeTypeDeclaration typeDeclaration, Field var codeTypeReference = memberInfo.FieldType.CreateCodeTypeReference(memberInfo); if (memberInfo.IsInitOnly) codeTypeReference = MakeReadonly(codeTypeReference); + if (IsUnsafeSignatureType(memberInfo.FieldType)) + codeTypeReference = MakeUnsafe(codeTypeReference); + var field = new CodeMemberField(codeTypeReference, memberInfo.Name) { Attributes = attributes, @@ -906,6 +928,11 @@ static CodeTypeReference MakeReadonly(CodeTypeReference typeReference) return ModifyCodeTypeReference(typeReference, "readonly"); } + static CodeTypeReference MakeUnsafe(CodeTypeReference typeReference) + { + return ModifyCodeTypeReference(typeReference, "unsafe"); + } + static CodeTypeReference ModifyCodeTypeReference(CodeTypeReference typeReference, string modifier) { using (var provider = new CSharpCodeProvider()) diff --git a/src/PublicApiGenerator/CSharpTypeKeyword.cs b/src/PublicApiGenerator/CSharpTypeKeyword.cs index 8172f73..45f4095 100644 --- a/src/PublicApiGenerator/CSharpTypeKeyword.cs +++ b/src/PublicApiGenerator/CSharpTypeKeyword.cs @@ -34,6 +34,8 @@ public static string Get(string typeName) return "string"; case "System.Boolean": return "bool"; + case "System.Void": + return "void"; default: return typeName; } diff --git a/src/PublicApiGenerator/CodeTypeReferenceBuilder.cs b/src/PublicApiGenerator/CodeTypeReferenceBuilder.cs index aada8e9..54e3e0e 100644 --- a/src/PublicApiGenerator/CodeTypeReferenceBuilder.cs +++ b/src/PublicApiGenerator/CodeTypeReferenceBuilder.cs @@ -144,6 +144,11 @@ static string GetTypeNameCore(TypeReference type, IEnumerator nullability return GetTypeName(array.ElementType, nullabilityMap, NullableMode.Default, disableNested) + "[]"; } + if (type is PointerType pointer) + { + return CSharpTypeKeyword.Get(GetTypeName(pointer.ElementType, nullabilityMap, NullableMode.Default, disableNested)) + "*"; + } + if (!type.IsNested || disableNested) { var name = type is RequiredModifierType modType ? modType.ElementType.Name : type.Name; diff --git a/src/PublicApiGeneratorTests/Field_modifiers.cs b/src/PublicApiGeneratorTests/Field_modifiers.cs index b150836..b15eed6 100644 --- a/src/PublicApiGeneratorTests/Field_modifiers.cs +++ b/src/PublicApiGeneratorTests/Field_modifiers.cs @@ -56,7 +56,7 @@ public void Include_const_fields() // Have to include the ctor - I can't figure out how to hide it // when values are initialized AssertPublicApi( -@"namespace PublicApiGeneratorTests.Examples + @"namespace PublicApiGeneratorTests.Examples { public class ClassWithConstFields { @@ -64,6 +64,26 @@ public class ClassWithConstFields public const int ConstPublicField = 42; public ClassWithConstFields() { } } +}"); + } + + [Fact] + public void Include_unsafe_fields() + { + // Have to include the ctor - I can't figure out how to hide it + // when values are initialized + AssertPublicApi( + @"namespace PublicApiGeneratorTests.Examples +{ + public class ClassWithUnsafeFields + { + protected unsafe void* UnsafeProtectedField; + public unsafe byte* UnsafePublicByteField; + public unsafe void* UnsafePublicField; + public unsafe int* UnsafePublicIntField; + public unsafe long* UnsafePublicLongField; + public ClassWithUnsafeFields() { } + } }"); } } @@ -94,6 +114,15 @@ public class ClassWithConstFields public const int ConstPublicField = 42; protected const string ConstProtectedField = "hello world"; } + + public class ClassWithUnsafeFields + { + public unsafe void* UnsafePublicField; + public unsafe byte* UnsafePublicByteField; + public unsafe int* UnsafePublicIntField; + public unsafe long* UnsafePublicLongField; + protected unsafe void* UnsafeProtectedField; + } } // ReSharper restore UnusedMember.Global // ReSharper restore ClassNeverInstantiated.Global diff --git a/src/PublicApiGeneratorTests/Method_modifiers.cs b/src/PublicApiGeneratorTests/Method_modifiers.cs index 15b6ff2..dc7f436 100644 --- a/src/PublicApiGeneratorTests/Method_modifiers.cs +++ b/src/PublicApiGeneratorTests/Method_modifiers.cs @@ -1,4 +1,4 @@ -using PublicApiGeneratorTests.Examples; +using PublicApiGeneratorTests.Examples; using Xunit; namespace PublicApiGeneratorTests @@ -86,6 +86,20 @@ public class ClassWithMethodHiding : PublicApiGeneratorTests.Examples.ClassWithS public ClassWithMethodHiding() { } public new void Method() { } } +}"); + } + + [Fact] + public void Should_output_unsafe_modifier() + { + AssertPublicApi( + @"namespace PublicApiGeneratorTests.Examples +{ + public class ClassWithUnsafeMethod + { + public ClassWithUnsafeMethod() { } + public unsafe void* DoSomething() { } + } }"); } } @@ -146,6 +160,14 @@ public override string ToString() return base.ToString(); } } + + public class ClassWithUnsafeMethod + { + public unsafe void* DoSomething() + { + return null; + } + } } // ReSharper restore ClassWithVirtualMembersNeverInherited.Global // ReSharper restore RedundantOverridenMember @@ -153,4 +175,4 @@ public override string ToString() // ReSharper restore UnusedMemberHierarchy.Global // ReSharper restore UnusedMember.Global // ReSharper restore ClassNeverInstantiated.Global -} \ No newline at end of file +} diff --git a/src/PublicApiGeneratorTests/PublicApiGeneratorTests.csproj b/src/PublicApiGeneratorTests/PublicApiGeneratorTests.csproj index b088829..e78d61a 100644 --- a/src/PublicApiGeneratorTests/PublicApiGeneratorTests.csproj +++ b/src/PublicApiGeneratorTests/PublicApiGeneratorTests.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.2 @@ -9,6 +9,7 @@ IDE0060 Remove unused parameter 'name' if it is not part of a shipped public API IDE1006 Naming rule violation: These words must begin with upper case characters --> + true