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

Propagate DefaultMemberAttribute to IndexerNameAttribute #136

Merged
merged 4 commits into from Nov 11, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
30 changes: 21 additions & 9 deletions src/PublicApiGenerator/ApiGenerator.cs
Expand Up @@ -180,6 +180,7 @@ static bool IsDotNetTypeMember(IMemberDefinition m, string[] whitelistedNamespac
}

static void AddMemberToTypeDeclaration(CodeTypeDeclaration typeDeclaration,
IMemberDefinition typeDeclarationInfo,
IMemberDefinition memberInfo,
AttributeFilter attributeFilter)
{
Expand All @@ -194,7 +195,7 @@ static bool IsDotNetTypeMember(IMemberDefinition m, string[] whitelistedNamespac
}
else if (memberInfo is PropertyDefinition propertyDefinition)
{
AddPropertyToTypeDeclaration(typeDeclaration, propertyDefinition, attributeFilter);
AddPropertyToTypeDeclaration(typeDeclaration, typeDeclarationInfo, propertyDefinition, attributeFilter);
}
else if (memberInfo is EventDefinition eventDefinition)
{
Expand Down Expand Up @@ -293,14 +294,14 @@ static CodeTypeDeclaration CreateTypeDeclaration(TypeDefinition publicType, stri
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);
AddMemberToTypeDeclaration(declaration, publicType, 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);
AddMemberToTypeDeclaration(declaration, publicType, field, attributeFilter);

foreach (var nestedType in publicType.NestedTypes.Where(ShouldIncludeType).OrderBy(t => t.FullName, StringComparer.Ordinal))
{
Expand Down Expand Up @@ -445,10 +446,7 @@ bool IsSpecialConstraint(GenericParameterConstraint constraint)
static CodeAttributeDeclaration GenerateCodeAttributeDeclaration(Func<CodeTypeReference, CodeTypeReference> codeTypeModifier, CustomAttribute customAttribute)
{
var attribute = new CodeAttributeDeclaration(codeTypeModifier(customAttribute.AttributeType.CreateCodeTypeReference(mode: NullableMode.Disable)));
if (attribute.Name != "System.ParamArrayAttribute")
{
attribute.Name = $"{attribute.Name}{CodeNormalizer.AttributeMarker}";
}
attribute.Name = AttributeNameBuilder.Get(attribute.Name);
foreach (var arg in customAttribute.ConstructorArguments)
{
attribute.Arguments.Add(new CodeAttributeArgument(CreateInitialiserExpression(arg)));
Expand Down Expand Up @@ -658,7 +656,7 @@ static object FormatParameterConstant(ParameterDefinition parameter)
return parameter.ParameterType.IsValueType ? "default" : "null";
}

static void AddPropertyToTypeDeclaration(CodeTypeDeclaration typeDeclaration, PropertyDefinition member, AttributeFilter attributeFilter)
static void AddPropertyToTypeDeclaration(CodeTypeDeclaration typeDeclaration, IMemberDefinition typeDeclarationInfo, PropertyDefinition member, AttributeFilter attributeFilter)
{
var getterAttributes = member.GetMethod?.GetMethodAttributes() ?? 0;
var setterAttributes = member.SetMethod?.GetMethodAttributes() ?? 0;
Expand All @@ -675,16 +673,30 @@ static void AddPropertyToTypeDeclaration(CodeTypeDeclaration typeDeclaration, Pr
? new CodeTypeReference(member.PropertyType.Name)
: member.PropertyType.CreateCodeTypeReference(member);

var propertyName = member.Name;
var property = new CodeMemberProperty
{
Name = member.Name,
Name = propertyName,
Type = propertyType,
Attributes = propertyAttributes,
CustomAttributes = CreateCustomAttributes(member, attributeFilter),
HasGet = hasGet,
HasSet = hasSet
};

// DefaultMemberAttribute on type gets propagated to IndexerNameAttribute
var defaultMemberAttribute = typeDeclarationInfo.CustomAttributes.SingleOrDefault(x =>
danielmarbach marked this conversation as resolved.
Show resolved Hide resolved
x.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
var defaultMemberAttributeArgument = defaultMemberAttribute?.ConstructorArguments.SingleOrDefault();
danielmarbach marked this conversation as resolved.
Show resolved Hide resolved
var value = defaultMemberAttributeArgument?.Value as string;
if (!string.IsNullOrEmpty(value) && propertyName != "Item")
danielmarbach marked this conversation as resolved.
Show resolved Hide resolved
{
property.Name = "Item";
danielmarbach marked this conversation as resolved.
Show resolved Hide resolved
var codeAttributeDeclaration = new CodeAttributeDeclaration(AttributeNameBuilder.Get("System.Runtime.CompilerServices.IndexerNameAttribute"));
codeAttributeDeclaration.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(value)));
property.CustomAttributes.Add(codeAttributeDeclaration);
danielmarbach marked this conversation as resolved.
Show resolved Hide resolved
}

// Here's a nice hack, because hey, guess what, the CodeDOM doesn't support
// attributes on getters or setters
if (member.GetMethod != null && member.GetMethod.HasCustomAttributes)
Expand Down
10 changes: 10 additions & 0 deletions src/PublicApiGenerator/AttributeNameBuilder.cs
@@ -0,0 +1,10 @@
namespace PublicApiGenerator
{
public static class AttributeNameBuilder
{
public static string Get(string name)
{
return name != "System.ParamArrayAttribute" ? $"{name}{CodeNormalizer.AttributeMarker}" : name;
danielmarbach marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
59 changes: 59 additions & 0 deletions src/PublicApiGeneratorTests/Indexer_properties.cs
@@ -0,0 +1,59 @@
using System.Runtime.CompilerServices;
using PublicApiGeneratorTests.Examples;
using Xunit;

namespace PublicApiGeneratorTests
{
public class Indexer_properties : ApiGeneratorTestsBase
{
[Fact]
public void Should_output_indexer()
{
AssertPublicApi<ClassWithIndexer>(
@"namespace PublicApiGeneratorTests.Examples
{
public class ClassWithIndexer
{
public ClassWithIndexer() { }
public int this[int x] { get; }
}
}");
}

[Fact]
public void Should_output_named_indexer()
{
AssertPublicApi<ClassWithNamedIndexer>(
@"namespace PublicApiGeneratorTests.Examples
{
public class ClassWithNamedIndexer
{
public ClassWithNamedIndexer() { }
[System.Runtime.CompilerServices.IndexerName(""Bar"")]
public int this[int x] { get; }
}
}");
}
}


// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ValueParameterNotUsed
namespace Examples
{
public class ClassWithIndexer
{
public int this[int x] => x;
}

public class ClassWithNamedIndexer
{
[IndexerName("Bar")]
public int this[int x] => x;
danielmarbach marked this conversation as resolved.
Show resolved Hide resolved
}
}
// ReSharper restore ValueParameterNotUsed
// ReSharper restore UnusedMember.Global
// ReSharper restore ClassNeverInstantiated.Global
}