Skip to content

Commit

Permalink
Include "associated constants" in enum declarations
Browse files Browse the repository at this point in the history
  • Loading branch information
AArnott committed Jan 11, 2024
1 parent 77ae66f commit 7f4d00f
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 13 deletions.
58 changes: 45 additions & 13 deletions src/Microsoft.Windows.CsWin32/Generator.Enum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,20 @@ private EnumDeclarationSyntax DeclareEnum(TypeDefinition typeDef)
TypeSyntax? enumBaseType = null;
foreach (FieldDefinitionHandle fieldDefHandle in typeDef.GetFields())
{
FieldDefinition fieldDef = this.Reader.GetFieldDefinition(fieldDefHandle);
string enumValueName = this.Reader.GetString(fieldDef.Name);
ConstantHandle valueHandle = fieldDef.GetDefaultValue();
if (valueHandle.IsNil)
AddEnumValue(fieldDefHandle);
}

// Add associated constants.
foreach (CustomAttribute associatedConstAtt in MetadataUtilities.FindAttributes(this.Reader, typeDef.GetCustomAttributes(), InteropDecorationNamespace, AssociatedConstantAttribute))
{
CustomAttributeValue<TypeSyntax> decodedAttribute = associatedConstAtt.DecodeValue(CustomAttributeTypeProvider.Instance);
if (decodedAttribute.FixedArguments.Length >= 1 && decodedAttribute.FixedArguments[0].Value is string constName)
{
enumBaseType = fieldDef.DecodeSignature(SignatureHandleProvider.Instance, null).ToTypeSyntax(this.enumTypeSettings, GeneratingElement.EnumValue, null).Type;
continue;
if (TryFindConstant(constName, out FieldDefinitionHandle fieldHandle))
{
AddEnumValue(fieldHandle);
}
}

bool enumBaseTypeIsSigned = enumBaseType is PredefinedTypeSyntax { Keyword: { RawKind: (int)SyntaxKind.LongKeyword or (int)SyntaxKind.IntKeyword or (int)SyntaxKind.ShortKeyword or (int)SyntaxKind.SByteKeyword } };
ExpressionSyntax enumValue = flagsEnum ? ToHexExpressionSyntax(this.Reader, valueHandle, enumBaseTypeIsSigned) : ToExpressionSyntax(this.Reader, valueHandle);
EnumMemberDeclarationSyntax enumMember = EnumMemberDeclaration(SafeIdentifier(enumValueName))
.WithEqualsValue(EqualsValueClause(enumValue));
enumValues.Add(enumMember);
enumValues.Add(TokenWithLineFeed(SyntaxKind.CommaToken));
}

if (enumBaseType is null)
Expand Down Expand Up @@ -58,5 +57,38 @@ private EnumDeclarationSyntax DeclareEnum(TypeDefinition typeDef)
result = this.AddApiDocumentation(name, result);

return result;

void AddEnumValue(FieldDefinitionHandle fieldDefHandle)
{
FieldDefinition fieldDef = this.Reader.GetFieldDefinition(fieldDefHandle);
string enumValueName = this.Reader.GetString(fieldDef.Name);
ConstantHandle valueHandle = fieldDef.GetDefaultValue();
if (valueHandle.IsNil)
{
enumBaseType = fieldDef.DecodeSignature(SignatureHandleProvider.Instance, null).ToTypeSyntax(this.enumTypeSettings, GeneratingElement.EnumValue, null).Type;
return;
}

bool enumBaseTypeIsSigned = enumBaseType is PredefinedTypeSyntax { Keyword: { RawKind: (int)SyntaxKind.LongKeyword or (int)SyntaxKind.IntKeyword or (int)SyntaxKind.ShortKeyword or (int)SyntaxKind.SByteKeyword } };
ExpressionSyntax enumValue = flagsEnum ? ToHexExpressionSyntax(this.Reader, valueHandle, enumBaseTypeIsSigned) : ToExpressionSyntax(this.Reader, valueHandle);
EnumMemberDeclarationSyntax enumMember = EnumMemberDeclaration(SafeIdentifier(enumValueName))
.WithEqualsValue(EqualsValueClause(enumValue));
enumValues.Add(enumMember);
enumValues.Add(TokenWithLineFeed(SyntaxKind.CommaToken));
}

bool TryFindConstant(string name, out FieldDefinitionHandle fieldDefinitionHandle)
{
foreach (var ns in this.MetadataIndex.MetadataByNamespace)
{
if (ns.Value.Fields.TryGetValue(name, out fieldDefinitionHandle))
{
return true;
}
}

fieldDefinitionHandle = default;
return false;
}
}
}
1 change: 1 addition & 0 deletions src/Microsoft.Windows.CsWin32/Generator.Invariants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public partial class Generator
internal const string RAIIFreeAttribute = "RAIIFreeAttribute";
internal const string DoNotReleaseAttribute = "DoNotReleaseAttribute";
internal const string AssociatedEnumAttribute = "AssociatedEnumAttribute";
internal const string AssociatedConstantAttribute = "AssociatedConstantAttribute";
internal const string GlobalNamespacePrefix = "global::";
internal const string GlobalWinmdRootNamespaceAlias = "winmdroot";
internal const string WinRTCustomMarshalerClass = "WinRTCustomMarshaler";
Expand Down
15 changes: 15 additions & 0 deletions src/Microsoft.Windows.CsWin32/MetadataUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,19 @@ internal static bool IsAttribute(MetadataReader reader, CustomAttribute attribut

return null;
}

internal static IEnumerable<CustomAttribute> FindAttributes(MetadataReader reader, CustomAttributeHandleCollection? customAttributeHandles, string attributeNamespace, string attributeName)
{
if (customAttributeHandles is not null)
{
foreach (CustomAttributeHandle handle in customAttributeHandles)
{
CustomAttribute att = reader.GetCustomAttribute(handle);
if (IsAttribute(reader, att, attributeNamespace, attributeName))
{
yield return att;
}
}
}
}
}
23 changes: 23 additions & 0 deletions test/Microsoft.Windows.CsWin32.Tests/EnumTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

public class EnumTests : GeneratorTestBase
{
public EnumTests(ITestOutputHelper logger)
: base(logger)
{
}

[Fact]
public void EnumsIncludeAssociatedConstants()
{
this.GenerateApi("SERVICE_ERROR");
EnumDeclarationSyntax enumDecl = Assert.IsType<EnumDeclarationSyntax>(this.FindGeneratedType("SERVICE_ERROR").Single());

// The enum should contain the constant.
Assert.Contains(enumDecl.Members, value => value.Identifier.ValueText == "SERVICE_NO_CHANGE");

// The constant should not be generated as a separate constant.
Assert.Empty(this.FindGeneratedConstant("SERVICE_NO_CHANGE"));
}
}

0 comments on commit 7f4d00f

Please sign in to comment.