Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unsafe modifier when necessary #120

Merged
merged 4 commits into from Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/PublicApiGenerator/ApiGenerator.cs
Expand Up @@ -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,
Expand All @@ -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");
Expand Down Expand Up @@ -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,
Expand All @@ -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())
Expand Down
2 changes: 2 additions & 0 deletions src/PublicApiGenerator/CSharpTypeKeyword.cs
Expand Up @@ -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;
}
Expand Down
5 changes: 5 additions & 0 deletions src/PublicApiGenerator/CodeTypeReferenceBuilder.cs
Expand Up @@ -144,6 +144,11 @@ static string GetTypeNameCore(TypeReference type, IEnumerator<bool?> 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;
Expand Down
31 changes: 30 additions & 1 deletion src/PublicApiGeneratorTests/Field_modifiers.cs
Expand Up @@ -56,14 +56,34 @@ public void Include_const_fields()
// Have to include the ctor - I can't figure out how to hide it
// when values are initialized
AssertPublicApi<ClassWithConstFields>(
@"namespace PublicApiGeneratorTests.Examples
@"namespace PublicApiGeneratorTests.Examples
{
public class ClassWithConstFields
{
protected const string ConstProtectedField = ""hello world"";
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<ClassWithUnsafeFields>(
@"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() { }
}
}");
}
}
Expand Down Expand Up @@ -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
Expand Down
26 changes: 24 additions & 2 deletions src/PublicApiGeneratorTests/Method_modifiers.cs
@@ -1,4 +1,4 @@
using PublicApiGeneratorTests.Examples;
using PublicApiGeneratorTests.Examples;
using Xunit;

namespace PublicApiGeneratorTests
Expand Down Expand Up @@ -86,6 +86,20 @@ public class ClassWithMethodHiding : PublicApiGeneratorTests.Examples.ClassWithS
public ClassWithMethodHiding() { }
public new void Method() { }
}
}");
}

[Fact]
public void Should_output_unsafe_modifier()
{
AssertPublicApi<ClassWithUnsafeMethod>(
@"namespace PublicApiGeneratorTests.Examples
{
public class ClassWithUnsafeMethod
{
public ClassWithUnsafeMethod() { }
public unsafe void* DoSomething() { }
}
}");
}
}
Expand Down Expand Up @@ -146,11 +160,19 @@ public override string ToString()
return base.ToString();
}
}

public class ClassWithUnsafeMethod
{
public unsafe void* DoSomething()
{
return null;
}
}
}
// ReSharper restore ClassWithVirtualMembersNeverInherited.Global
// ReSharper restore RedundantOverridenMember
// ReSharper restore MemberCanBeProtected.Global
// ReSharper restore UnusedMemberHierarchy.Global
// ReSharper restore UnusedMember.Global
// ReSharper restore ClassNeverInstantiated.Global
}
}
3 changes: 2 additions & 1 deletion src/PublicApiGeneratorTests/PublicApiGeneratorTests.csproj
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
Expand All @@ -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
-->
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
Expand Down