@@ -58,12 +58,11 @@ enums.
5858``` cpp
5959#ifdef _WIN32METADATA_
6060
61- enum SET_THEME_APP_PROPERTIES_FLAGS : unsigned int {
61+ enum __ attribute __ ((flag_enum)) SET_THEME_APP_PROPERTIES_FLAGS : unsigned int {
6262 ALLOW_NONCLIENT = STAP_ALLOW_NONCLIENT,
6363 ALLOW_CONTROLS = STAP_ALLOW_CONTROLS,
6464 ALLOW_WEBCONTENT = STAP_ALLOW_WEBCONTENT,
6565};
66- DEFINE_ENUM_FLAG_OPERATORS(SET_THEME_APP_PROPERTIES_FLAGS)
6766
6867#else
6968#define SET_THEME_APP_PROPERTIES_FLAGS DWORD
@@ -91,8 +90,11 @@ Key points:
9190- `#else #define NAME DWORD` — the enum name becomes a DWORD macro for normal
9291 compilation, allowing unconditional use in function signatures
9392- Modern C++ syntax — `enum NAME : base_type`, no `typedef` needed
94- - `DEFINE_ENUM_FLAG_OPERATORS(NAME)` — existing SDK convention for flags enums,
95- already detected by the ConstantsScraper (no new annotation needed)
93+ - `__attribute__((flag_enum))` — Clang's standard attribute for bitmask enums
94+ (see [Clang Attribute Reference](https://clang.llvm.org/docs/AttributeReference.html#flag-enum)).
95+ The Win32MetadataScraper detects this attribute in the AST and injects
96+ `[Flags]` via PInvokeGenerator's `WithAttributes` mechanism — fully through the
97+ AST, no text parsing or sidecar RSP entries needed
9698- Member values reference the original `#define` macros directly — the preprocessor
9799 computes the actual values
98100
@@ -104,11 +106,15 @@ All scenarios were tested with ClangSharp v17 and v18 (test files in
104106| Scenario | ClangSharp v17 | ClangSharp v18 |
105107|---|---|---|
106108| Enum declarations | ✅ | ✅ |
107- | `[[clang::annotate("w32m:flags")]] ` | ❌ (dropped ) | ✅ `[NativeAnnotation]` |
109+ | `__attribute__((flag_enum))` → `[Flags] ` | ✅ (via tooling ) | ✅ (via tooling) |
108110| C++11 enum base types (`: unsigned char`, etc.) | ✅ `enum X : byte` | ✅ |
109111| Signed overflow (`(int)0xFFFFFFFF`) | ✅ auto `unchecked()` | ✅ |
110112| Function params get enum type | ✅ | ✅ |
111- | `#pragma clang attribute push` for flags | ❌ (dropped) | ✅ |
113+
114+ Note: `__attribute__((flag_enum))` is present in the Clang AST on both v17 and v18
115+ (as `CX_AttrKind_FlagEnum`), but ClangSharp's PInvokeGenerator doesn't handle it
116+ natively. The Win32MetadataScraper detects it during the AST walk and passes it to
117+ PInvokeGenerator via `WithAttributes`, which works on all versions.
112118
113119### Three member patterns
114120
@@ -134,7 +140,8 @@ Use `push_macro`/`undef`/`pop_macro` to suppress macro expansion inside the enum
134140#undef FILE_ATTRIBUTE_READONLY
135141#undef FILE_ATTRIBUTE_HIDDEN
136142
137- enum [[clang::annotate("w32m:flags")]] FILE_ATTRIBUTE_FLAGS : unsigned int {
143+ ```cpp
144+ enum __attribute__ ((flag_enum)) FILE_ATTRIBUTE_FLAGS : unsigned int {
138145 FILE_ATTRIBUTE_READONLY = 0x00000001,
139146 FILE_ATTRIBUTE_HIDDEN = 0x00000002,
140147};
@@ -159,28 +166,44 @@ enum OBJECT_IDENTIFIER : int {
159166
160167### Flags annotation
161168
162- There is no C++ standard ` [[bitmask]] ` attribute. We define a SAL-like macro:
169+ Clang provides ` __attribute__((flag_enum)) ` to mark enums as bitmask types (see
170+ [ Clang Attribute Reference] ( https://clang.llvm.org/docs/AttributeReference.html#flag-enum ) ).
171+ There is no C++ standard equivalent — no ` [[bitmask]] ` attribute exists even in C++26.
163172
164- ``` cpp
165- // In win32metadata.h
166- #ifdef _WIN32METADATA_
167- #define _ Enum_flags_ [[ clang::annotate("w32m: flags ")]]
168- #else
169- #define _ Enum_flags_
170- #endif
173+ The Win32MetadataScraper detects ` flag_enum ` during the AST walk by checking
174+ ` EnumDecl.Attrs ` for ` CX_AttrKind_FlagEnum ` . Discovered flag enums are injected into
175+ PInvokeGenerator's ` WithAttributes ` as ` [Flags] ` :
176+
177+ ``` csharp
178+ // In RemapDiscovery.WalkDecl:
179+ if (decl is EnumDecl enumDecl && enumDecl .HasAttrs )
180+ {
181+ foreach (var attr in enumDecl .Attrs )
182+ {
183+ if (attr .Kind == CX_AttrKind_FlagEnum )
184+ result .FlagEnumNames .Add (enumDecl .Name );
185+ }
186+ }
187+
188+ // In Program.cs, before creating PInvokeGenerator config:
189+ foreach (var flagEnum in discovery .FlagEnumNames )
190+ withAttributes [flagEnum ] = new List <string > { " Flags" };
171191```
172192
173- Usage:
193+ Usage in headers :
174194``` cpp
175- enum _Enum_flags_ FILE_ATTRIBUTE_FLAGS : unsigned int { ... };
195+ enum __attribute__ ((flag_enum)) FILE_ATTRIBUTE_FLAGS : unsigned int { ... };
176196```
177197
178- On v18+, this emits `[NativeAnnotation("w32m:flags")]` on the enum, which the emitter
179- converts to `[Flags]`.
198+ This approach:
199+ - Flows entirely through the Clang AST — no text parsing
200+ - Works on both ClangSharp v17 and v18
201+ - Requires no sidecar RSP entries
202+ - Uses a standard Clang attribute rather than a custom annotation
180203
181- As a fallback, the existing `DEFINE_ENUM_FLAG_OPERATORS(FOO)` macro is already detected
182- by the ConstantsScraper ( text-level regex) and written to `enumsMakeFlags.generated.rsp`.
183- Both mechanisms can coexist .
204+ For SDK headers that already use `DEFINE_ENUM_FLAG_OPERATORS(FOO)`, the existing
205+ ConstantsScraper text-detection path ( `enumsMakeFlags.generated.rsp`) continues to
206+ work as a fallback .
184207
185208### Underlying types
186209
@@ -307,7 +330,7 @@ Every field in enums.json maps to a header construct:
307330| enums.json field | Header equivalent |
308331| ---| ---|
309332| ` name ` | ` enum NAME ` |
310- | ` flags ` | ` _Enum_flags_ ` / ` [[clang::annotate("w32m:flags")]] ` |
333+ | ` flags ` | ` __attribute__((flag_enum)) ` on enum |
311334| ` type ` | C++ enum base (` : unsigned int ` , ` : unsigned char ` , etc.) |
312335| ` members[].name ` | Enum member identifier |
313336| ` members[].value ` | Literal value or ` #define ` macro reference |
@@ -321,10 +344,9 @@ Every field in enums.json maps to a header construct:
321344
322345## Implementation steps
323346
324- 1 . ** Upgrade ClangSharp to v18+** — required for ` [[clang::annotate]] ` on enums
325- 2 . ** Build a migration tool** — converts each ` enums.json ` entry into a conditional
326- enum declaration block
327- 3 . ** Create overlay headers** with the generated declarations
347+ 1 . ** Build a migration tool** — converts each ` enums.json ` entry into a conditional
348+ enum declaration block with ` __attribute__((flag_enum)) ` where appropriate
349+ 2 . ** Create overlay headers** with the generated declarations
3283504 . ** Add ` _Enum_type_() ` usage annotations** — from ` enums.json ` ` uses ` array
3293515 . ** Validate winmd equivalence** — compare winmd output before/after
3303526 . ** Remove ` enums.json ` ** and simplify ConstantsScraper
0 commit comments