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

Handle DisplayAttribute. #13

Merged
merged 27 commits into from
Apr 18, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e4ec4b0
Handle DisplayAttribute.
adamradocz Mar 18, 2022
af48c97
Fix indentation.
adamradocz Mar 19, 2022
984db87
Add comment.
adamradocz Mar 19, 2022
aa79449
Update the test.
adamradocz Mar 23, 2022
c860798
Update naming.
Mar 31, 2022
b3ef77e
Add System.ComponentModel.Annotations package.
adamradocz Apr 1, 2022
da5f8e1
Add Integration test
adamradocz Apr 2, 2022
834aa69
Fix CS8604 warning.
adamradocz Apr 2, 2022
5f86ecf
Fix: Warning CS8600 Converting null literal or possible null value t…
adamradocz Apr 2, 2022
7c25653
Remove the need of Linq
adamradocz Apr 2, 2022
a061571
Add conditions to System.ComponentModel.Annotations
adamradocz Apr 2, 2022
0637220
Update tests/NetEscapades.EnumGenerators.IntegrationTests/ExtensionTe…
adamradocz Apr 2, 2022
1cb40d0
Fix: typeof(value) -> typeof(T)
adamradocz Apr 2, 2022
ab54aff
Remove unnecessary package.
adamradocz Apr 2, 2022
9289b5c
Add allowMatchingDisplayAttribute to IsDefined.
adamradocz Apr 3, 2022
b9bf7ca
Add allowMatchingDisplayAttribute optsion to TryParse
adamradocz Apr 3, 2022
7de7f7d
Add SameDisplayName tests.
adamradocz Apr 3, 2022
4b1e5ce
Add benchmarks.
adamradocz Apr 3, 2022
41b01eb
Fix naming.
adamradocz Apr 3, 2022
fd6f363
Update IsDefinedNameBenchmark
adamradocz Apr 4, 2022
7411235
Update TryParseBenchmark
adamradocz Apr 4, 2022
a883759
Switch from optional parameter to overload in case of IsDefined and T…
Apr 7, 2022
64caf0c
Update tests
Apr 7, 2022
7bbbf29
Update tests.
Apr 7, 2022
b8f01ed
TryParse ignore case optimization
Apr 7, 2022
cc2c3ae
Update IsDefined
adamradocz Apr 7, 2022
83d76a7
Update naming.
adamradocz Apr 8, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ To use the generator, add the `[EnumExtensions]` attribute to an enum. For examp
public MyEnum
{
First,

[Display(Name = "2nd")]
Second,
}
```
Expand All @@ -56,7 +58,7 @@ public static partial class MyEnumExtensions
=> value switch
{
MyEnum.First => nameof(MyEnum.First),
MyEnum.Second => nameof(MyEnum.Second),
MyEnum.Second => "2nd",
_ => value.ToString(),
};

Expand Down
24 changes: 21 additions & 3 deletions src/NetEscapades.EnumGenerators/EnumGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ static List<EnumToGenerate> GetTypesToGenerate(Compilation compilation, IEnumera
string underlyingType = enumSymbol.EnumUnderlyingType?.ToString() ?? "int";

var enumMembers = enumSymbol.GetMembers();
var members = new List<KeyValuePair<string, object>>(enumMembers.Length);
var members = new List<KeyValuePair<string, string?>>(enumMembers.Length);

foreach (var member in enumMembers)
{
Expand All @@ -159,7 +159,25 @@ static List<EnumToGenerate> GetTypesToGenerate(Compilation compilation, IEnumera
continue;
}

members.Add(new KeyValuePair<string, object>(member.Name, field.ConstantValue));
string? displayName = null;
foreach (var attribute in member.GetAttributes())
{
if (attribute.AttributeClass is null || attribute.AttributeClass.Name != "DisplayAttribute")
{
continue;
}

foreach (var namedArgument in attribute.NamedArguments)
{
if (namedArgument.Key == "Name" && namedArgument.Value.Value?.ToString() is { } dn)
{
displayName = dn;
break;
}
}
}

members.Add(new KeyValuePair<string, string?>(member.Name, displayName));
}

enumsToGenerate.Add(new EnumToGenerate(
Expand All @@ -169,7 +187,7 @@ static List<EnumToGenerate> GetTypesToGenerate(Compilation compilation, IEnumera
underlyingType: underlyingType,
isPublic: enumSymbol.DeclaredAccessibility == Accessibility.Public,
hasFlags: hasFlags,
values: members));
names: members));
}

return enumsToGenerate;
Expand Down
10 changes: 7 additions & 3 deletions src/NetEscapades.EnumGenerators/EnumToGenerate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,25 @@ public readonly struct EnumToGenerate
public readonly bool IsPublic;
public readonly bool HasFlags;
public readonly string UnderlyingType;
public readonly List<KeyValuePair<string, object>> Values;

/// <summary>
/// Key is the enum name. Value is the custom name setted by the <c>[Display(Name)]</c> attribute.
/// </summary>
public readonly List<KeyValuePair<string, string?>> Names;

public EnumToGenerate(
string name,
string ns,
string fullyQualifiedName,
string underlyingType,
bool isPublic,
List<KeyValuePair<string, object>> values,
List<KeyValuePair<string, string?>> names,
bool hasFlags)
{
Name = name;
Namespace = ns;
UnderlyingType = underlyingType;
Values = values;
Names = names;
HasFlags = hasFlags;
IsPublic = isPublic;
FullyQualifiedName = fullyQualifiedName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" Condition="'$(TargetFramework)' == 'net48'" />
adamradocz marked this conversation as resolved.
Show resolved Hide resolved
<ProjectReference Include="..\NetEscapades.EnumGenerators.Attributes\NetEscapades.EnumGenerators.Attributes.csproj" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<None Include="../../README.md" Pack="true" PackagePath="\"/>
<None Include="../../README.md" Pack="true" PackagePath="\" />
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(OutputPath)\NetEscapades.EnumGenerators.Attributes.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(OutputPath)\NetEscapades.EnumGenerators.Attributes.dll" Pack="true" PackagePath="lib\netstandard2.0" Visible="true" />
Expand Down
26 changes: 17 additions & 9 deletions src/NetEscapades.EnumGenerators/SourceGenerationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,20 @@ namespace ").Append(enumToGenerate.Namespace).Append(@"
public static string ToStringFast(this ").Append(enumToGenerate.FullyQualifiedName).Append(@" value)
=> value switch
{");
foreach (var member in enumToGenerate.Values)
foreach (var member in enumToGenerate.Names)
{
sb.Append(@"
").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key)
.Append(" => nameof(")
.Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append("),");
.Append(" => ");

if (member.Value is null)
{
sb.Append("nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append("),");
}
else
{
sb.Append('"').Append(member.Value).Append(@""",");
}
}

sb.Append(@"
Expand All @@ -90,7 +98,7 @@ public static bool HasFlag(this ").Append(enumToGenerate.FullyQualifiedName).App
public static bool IsDefined(").Append(enumToGenerate.FullyQualifiedName).Append(@" value)
=> value switch
{");
foreach (var member in enumToGenerate.Values)
foreach (var member in enumToGenerate.Names)
{
sb.Append(@"
").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key)
Expand All @@ -103,7 +111,7 @@ public static bool IsDefined(").Append(enumToGenerate.FullyQualifiedName).Append
public static bool IsDefined(string name)
=> name switch
{");
foreach (var member in enumToGenerate.Values)
foreach (var member in enumToGenerate.Names)
{
sb.Append(@"
nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@") => true,");
Expand Down Expand Up @@ -131,7 +139,7 @@ private static bool TryParseIgnoreCase(
{
switch (name)
{");
foreach (var member in enumToGenerate.Values)
foreach (var member in enumToGenerate.Names)
{
sb.Append(@"
case { } s when s.Equals(nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"), System.StringComparison.OrdinalIgnoreCase):
Expand All @@ -158,7 +166,7 @@ public static bool TryParse(
{
switch (name)
{");
foreach (var member in enumToGenerate.Values)
foreach (var member in enumToGenerate.Names)
{
sb.Append(@"
case nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(@"):
Expand All @@ -180,7 +188,7 @@ public static bool TryParse(
{
return new[]
{");
foreach (var member in enumToGenerate.Values)
foreach (var member in enumToGenerate.Names)
{
sb.Append(@"
").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append(',');
Expand All @@ -193,7 +201,7 @@ public static string[] GetNames()
{
return new[]
{");
foreach (var member in enumToGenerate.Values)
foreach (var member in enumToGenerate.Names)
{
sb.Append(@"
nameof(").Append(enumToGenerate.FullyQualifiedName).Append('.').Append(member.Key).Append("),");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Xunit;

namespace NetEscapades.EnumGenerators.IntegrationTests;

public class EnumWithDisplayNameInNamespaceExtensionsTests : ExtensionTests<EnumWithDisplayNameInNamespace>
{
public static TheoryData<EnumWithDisplayNameInNamespace> ValidEnumValues() => new()
{
EnumWithDisplayNameInNamespace.First,
EnumWithDisplayNameInNamespace.Second,
(EnumWithDisplayNameInNamespace)3,
};

public static TheoryData<string> ValuesToParse() => new()
{
"First",
"Second",
"first",
"SECOND",
"3",
"267",
"-267",
"2147483647",
"3000000000",
"Fourth",
"Fifth",
};
adamradocz marked this conversation as resolved.
Show resolved Hide resolved

protected override string ToStringFast(EnumWithDisplayNameInNamespace value) => value.ToStringFast();
protected override bool IsDefined(EnumWithDisplayNameInNamespace value) => EnumWithDisplayNameInNamespaceExtensions.IsDefined(value);
protected override bool IsDefined(string name) => EnumWithDisplayNameInNamespaceExtensions.IsDefined(name);
protected override bool TryParse(string name,bool ignoreCase, out EnumWithDisplayNameInNamespace parsed)
=> EnumWithDisplayNameInNamespaceExtensions.TryParse(name, ignoreCase, out parsed);

[Theory]
[MemberData(nameof(ValidEnumValues))]
public void GeneratesToStringFast(EnumWithDisplayNameInNamespace value) => GeneratesToStringFastTest(value);

[Theory]
[MemberData(nameof(ValidEnumValues))]
public void GeneratesIsDefined(EnumWithDisplayNameInNamespace value) => GeneratesIsDefinedTest(value);

[Theory]
[MemberData(nameof(ValuesToParse))]
public void GeneratesIsDefinedUsingName(string name) => GeneratesIsDefinedTest(name);

[Theory]
[MemberData(nameof(ValuesToParse))]
public void GeneratesTryParse(string name) => GeneratesTryParseTest(name);

[Theory]
[MemberData(nameof(ValuesToParse))]
public void GeneratesTryParseIgnoreCase(string name) => GeneratesTryParseIgnoreCaseTest(name);

[Fact]
public void GeneratesGetValues() => GeneratesGetValuesTest(EnumWithDisplayNameInNamespaceExtensions.GetValues());

[Fact]
public void GeneratesGetNames() => base.GeneratesGetNamesTest(EnumWithDisplayNameInNamespaceExtensions.GetNames());
}
12 changes: 12 additions & 0 deletions tests/NetEscapades.EnumGenerators.IntegrationTests/Enums.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.ComponentModel.DataAnnotations;

namespace NetEscapades.EnumGenerators.IntegrationTests
{
Expand All @@ -9,6 +10,17 @@ public enum EnumInNamespace
Second = 1,
Third = 2,
}

[EnumExtensions]
public enum EnumWithDisplayNameInNamespace
{
First = 0,

[Display(Name = "2nd")]
Second = 1,

Third = 2,
}

[EnumExtensions]
public enum LongEnum: long
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using FluentAssertions;
using FluentAssertions.Execution;

namespace NetEscapades.EnumGenerators.IntegrationTests;

#nullable enable
public abstract class ExtensionTests<T> where T : struct
{
protected abstract string ToStringFast(T value);
Expand All @@ -14,8 +17,23 @@ public abstract class ExtensionTests<T> where T : struct
protected void GeneratesToStringFastTest(T value)
{
var serialized = ToStringFast(value);
var valueAsString = value.ToString();
string? displayName = null;

serialized.Should().Be(value.ToString());
if (typeof(T).IsEnum)
{
if (valueAsString is not null)
{// Prevent: Warning CS8604 Possible null reference argument for parameter 'name' in 'MemberInfo[] Type.GetMember(string name)'
var memberInfo = value.GetType().GetMember(valueAsString);
adamradocz marked this conversation as resolved.
Show resolved Hide resolved
if (memberInfo.Length > 0)
{
displayName = memberInfo[0].GetCustomAttribute<DisplayAttribute>()?.GetName();
}
}
}

var expectedValue = displayName is null ? valueAsString : displayName;
serialized.Should().Be(expectedValue);
}

protected void GeneratesIsDefinedTest(T value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\NetEscapades.EnumGenerators\NetEscapades.EnumGenerators.csproj"
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\src\NetEscapades.EnumGenerators.Attributes\NetEscapades.EnumGenerators.Attributes.csproj"
OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
<ProjectReference Include="..\..\src\NetEscapades.EnumGenerators\NetEscapades.EnumGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\src\NetEscapades.EnumGenerators.Attributes\NetEscapades.EnumGenerators.Attributes.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
</ItemGroup>

<ItemGroup>
Expand All @@ -21,6 +19,7 @@
<PackageReference Include="FluentAssertions" Version="6.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" Condition="'$(TargetFramework)' == 'net48'" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@
</PropertyGroup>

<ItemGroup>
<Compile Include="..\NetEscapades.EnumGenerators.IntegrationTests\*.cs" Link="%(Filename)%(Extension)"/>
<Compile Include="..\NetEscapades.EnumGenerators.IntegrationTests\*.cs" Link="%(Filename)%(Extension)" />
</ItemGroup>

<ItemGroup>
<Content Include="..\NetEscapades.EnumGenerators.IntegrationTests\xunit.runner.json" CopyToOutputDirectory="PreserveNewest"/>
<Content Include="..\NetEscapades.EnumGenerators.IntegrationTests\xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="NetEscapades.EnumGenerators" Version="$(Version)" ExcludeAssets="compile;runtime" PrivateAssets="all"/>
<PackageReference Include="NetEscapades.EnumGenerators" Version="$(Version)" ExcludeAssets="compile;runtime" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0"/>
<PackageReference Include="xunit" Version="2.4.1"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" Condition="'$(TargetFramework)' == 'net48'" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,22 @@
</PropertyGroup>

<ItemGroup>
<Compile Include="..\NetEscapades.EnumGenerators.IntegrationTests\*.cs" Link="%(Filename)%(Extension)"/>
<Compile Include="..\NetEscapades.EnumGenerators.IntegrationTests\*.cs" Link="%(Filename)%(Extension)" />
</ItemGroup>

<ItemGroup>
<Content Include="..\NetEscapades.EnumGenerators.IntegrationTests\xunit.runner.json" CopyToOutputDirectory="PreserveNewest"/>
<Content Include="..\NetEscapades.EnumGenerators.IntegrationTests\xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="NetEscapades.EnumGenerators" Version="$(Version)" PrivateAssets="All"/>
<PackageReference Include="NetEscapades.EnumGenerators" Version="$(Version)" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0"/>
<PackageReference Include="xunit" Version="2.4.1"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" Condition="'$(TargetFramework)' == 'net48'" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand Down
Loading