Beautify enum declarations by referencing previously declared members - #3902
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves enum decompilation output by emitting member references (and [Flags] unions/complements) instead of always printing raw numeric literals, while enforcing ordering/compilability rules for enum member initializers.
Changes:
- Extend
TypeSystemAstBuilder.ConvertEnumValueto support enum-member-initializer context (unqualified references, avoid self/forward references, suppress non-compiling complements). - Update enum field decompilation to use the enhanced enum-value conversion for standard integral underlying types.
- Add/extend pretty-test fixtures to pin the new enum formatting behavior across flags, duplicates, masks, complements, and underlying types.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs | Implements the enum “beautification” logic (member references, flags unions, initializer-specific guardrails). |
| ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs | Switches enum member initializer generation to use ConvertEnumValue when safe, falling back for unusual underlying types. |
| ICSharpCode.Decompiler.Tests/TestCases/Pretty/EnumTests.cs | Adds fixtures covering duplicates, flags unions, complement handling, mask-family heuristics, and underlying-type edge cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
siegfriedpammer
force-pushed
the
enum-member-references
branch
from
July 22, 2026 05:28
e2d8f6c to
1c21c83
Compare
Enum members whose value duplicates an earlier member now reference it (Item2B = Item2A), [Flags] members combine earlier single-bit members (All = Item1 | Item2 | Item3) or their complement (NotItem1 = ~Item1) instead of showing a bare number. Several guardrails keep the output faithful to how such enums are written by hand: only previously declared members are referenced (field row order); a multi-bit value lying entirely within a larger, earlier member is a field encoding inside that mask, not a flag union, and stays numeric, as do zero-valued members of [Flags] enums, which routinely have several unrelated zero members. The ~X form is suppressed in byte/ushort enum declarations, where the initializer constant folds in int and would not compile. Enums with unusual underlying types (bool, native int) keep the plain constant conversion. With these rules, decompiling System.Private.CoreLib reproduces the hand-written declarations of TypeAttributes, MethodAttributes, AttributeTargets and FileAttributes almost verbatim. Assisted-by: Claude:claude-fable-5:Claude Code
siegfriedpammer
force-pushed
the
enum-member-references
branch
from
July 22, 2026 05:33
1c21c83 to
3b53feb
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Enum declarations are decompiled with every member shown as a bare number, even when the original source referenced other members:
All = 7instead ofAll = Item1 | Item2 | Item3,Item2B = 2instead ofItem2B = Item2A. (No open issue; this revives and completes the oldstash/beautify-enum-member-declarationsexperiment.)Solution
Enum member initializers that duplicate an earlier member now reference it, and combined
[Flags]values are shown as a union of their earlier single-bit members or as a complement:Guardrails keep the output faithful to how such enums are written by hand:
[Flags]enums stay numeric too (mask-style enums routinely have several unrelated zero members, e.g.MethodAttributes.PrivateScope/ReuseSlot).~Xcomplement form is suppressed in byte/ushort enum declarations, where the initializer constant folds inintand would not compile (CS0031); expression contexts keep it for every underlying type.With these rules, decompiling System.Private.CoreLib reproduces the hand-written declarations of
TypeAttributes,MethodAttributes,FileAttributesandAttributeTargets(includingAll = Assembly | Module | ... | GenericParameter) almost verbatim: numeric exactly where the original sources are numeric, while unambiguous aliases such asNewSlot = VtableLayoutMaskare referenced.Most in need of attention: the two output-policy heuristics (mask-encoding suppression and numeric zeros in
[Flags]declarations) are aesthetic trade-offs; the fixtures pin the chosen behavior.EnumTestscover mask-family enums, unsigned/byte/short flags, complements at declaration and usage sites, and zero/non-flags duplicates; full suite green (all non-Correctness tests 2171 passed, Correctness 197 passed).The code and this description were largely written by an AI agent (Claude Code), reviewed by me.
🤖 Generated with Claude Code