Browse Source

Currently implemented generators and supporting types such as their attributes

Brandon Thetford 1 year ago
parent
commit
e3d8f476f8

+ 117 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Analyzers/GenerateEnumExtensionMethodsAttributeAnalyzer.cs

@@ -0,0 +1,117 @@
+#define JETBRAINS_ANNOTATIONS
+using System.Collections.Immutable;
+using System.Linq;
+using JetBrains.Annotations;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Terminal.Gui.Analyzers.Internal.Attributes;
+using Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
+
+namespace Terminal.Gui.Analyzers.Internal.Analyzers;
+
+/// <summary>
+///     Design-time analyzer that checks for proper use of <see cref="GenerateEnumExtensionMethodsAttribute"/>.
+/// </summary>
+[DiagnosticAnalyzer (LanguageNames.CSharp)]
+[UsedImplicitly]
+internal sealed class GenerateEnumExtensionMethodsAttributeAnalyzer : DiagnosticAnalyzer
+{
+    // ReSharper disable once InconsistentNaming
+    private static readonly DiagnosticDescriptor TG0001_GlobalNamespaceNotSupported = new (
+                                                                                           // ReSharper restore InconsistentNaming
+                                                                                           "TG0001",
+                                                                                           $"{nameof (GenerateEnumExtensionMethodsAttribute)} not supported on global enums",
+                                                                                           "{0} is in the global namespace, which is not supported by the source generator ({1}) used by {2}. Move the enum to a namespace or remove the attribute.",
+                                                                                           "Usage",
+                                                                                           DiagnosticSeverity.Error,
+                                                                                           true,
+                                                                                           null,
+                                                                                           null,
+                                                                                           WellKnownDiagnosticTags.NotConfigurable,
+                                                                                           WellKnownDiagnosticTags.Compiler);
+
+    // ReSharper disable once InconsistentNaming
+    private static readonly DiagnosticDescriptor TG0002_UnderlyingTypeNotSupported = new (
+                                                                                          "TG0002",
+                                                                                          $"{nameof (GenerateEnumExtensionMethodsAttribute)} not supported for this enum type",
+                                                                                          "{0} has an underlying type of {1}, which is not supported by the source generator ({2}) used by {3}. Only enums backed by int or uint are supported.",
+                                                                                          "Usage",
+                                                                                          DiagnosticSeverity.Error,
+                                                                                          true,
+                                                                                          null,
+                                                                                          null,
+                                                                                          WellKnownDiagnosticTags.NotConfigurable,
+                                                                                          WellKnownDiagnosticTags.Compiler);
+
+    /// <inheritdoc/>
+    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
+        [
+            TG0001_GlobalNamespaceNotSupported,
+            TG0002_UnderlyingTypeNotSupported
+        ];
+
+    /// <inheritdoc/>
+    public override void Initialize (AnalysisContext context)
+    {
+        context.ConfigureGeneratedCodeAnalysis (GeneratedCodeAnalysisFlags.None);
+        context.EnableConcurrentExecution ();
+
+        context.RegisterSyntaxNodeAction (CheckAttributeLocations, SyntaxKind.EnumDeclaration);
+
+        return;
+
+        static void CheckAttributeLocations (SyntaxNodeAnalysisContext analysisContext)
+        {
+            ISymbol? symbol = analysisContext.SemanticModel.GetDeclaredSymbol (analysisContext.Node) as INamedTypeSymbol;
+
+            if (symbol is not INamedTypeSymbol { EnumUnderlyingType: { } } enumSymbol)
+            {
+                // Somehow not even an enum declaration.
+                // Skip it.
+                return;
+            }
+
+            // Check attributes for those we care about and react accordingly.
+            foreach (AttributeData attributeData in enumSymbol.GetAttributes ())
+            {
+                if (attributeData.AttributeClass?.Name != nameof (GenerateEnumExtensionMethodsAttribute))
+                {
+                    // Just skip - not an interesting attribute.
+                    continue;
+                }
+
+                // Check enum underlying type for supported types (int and uint, currently)
+                // Report TG0002 if unsupported underlying type.
+                if (enumSymbol.EnumUnderlyingType is not { SpecialType: SpecialType.System_Int32 or SpecialType.System_UInt32 })
+                {
+                    analysisContext.ReportDiagnostic (
+                                                      Diagnostic.Create (
+                                                                         TG0002_UnderlyingTypeNotSupported,
+                                                                         enumSymbol.Locations.FirstOrDefault (),
+                                                                         enumSymbol.Name,
+                                                                         enumSymbol.EnumUnderlyingType.Name,
+                                                                         nameof (EnumExtensionMethodsIncrementalGenerator),
+                                                                         nameof (GenerateEnumExtensionMethodsAttribute)
+                                                                        )
+                                                     );
+                }
+
+                // Check enum namespace (only non-global supported, currently)
+                // Report TG0001 if in the global namespace.
+                if (enumSymbol.ContainingSymbol is not INamespaceSymbol { IsGlobalNamespace: false })
+                {
+                    analysisContext.ReportDiagnostic (
+                                                      Diagnostic.Create (
+                                                                         TG0001_GlobalNamespaceNotSupported,
+                                                                         enumSymbol.Locations.FirstOrDefault (),
+                                                                         enumSymbol.Name,
+                                                                         nameof (EnumExtensionMethodsIncrementalGenerator),
+                                                                         nameof (GenerateEnumExtensionMethodsAttribute)
+                                                                        )
+                                                     );
+                }
+            }
+        }
+    }
+}

+ 26 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/AssemblyExtendedEnumTypeAttribute.cs

@@ -0,0 +1,26 @@
+// ReSharper disable ClassNeverInstantiated.Global
+#nullable enable
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>Assembly attribute declaring a known pairing of an <see langword="enum" /> type to an extension class.</summary>
+/// <remarks>This attribute should only be written by internal source generators for Terminal.Gui. No other usage of any kind is supported.</remarks>
+[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true)]
+internal sealed class AssemblyExtendedEnumTypeAttribute : System.Attribute
+{
+    /// <summary>Creates a new instance of <see cref="AssemblyExtendedEnumTypeAttribute" /> from the provided parameters.</summary>
+    /// <param name="enumType">The <see cref="System.Type" /> of an <see langword="enum" /> decorated with a <see cref="GenerateEnumExtensionMethodsAttribute" />.</param>
+    /// <param name="extensionClass">The <see cref="System.Type" /> of the <see langword="class" /> decorated with an <see cref="ExtensionsForEnumTypeAttribute{TEnum}" /> referring to the same type as <paramref name="enumType" />.</param>
+    public AssemblyExtendedEnumTypeAttribute (System.Type enumType, System.Type extensionClass)
+    {
+        EnumType = enumType;
+        ExtensionClass = extensionClass;
+    }
+    ///<summary>An <see langword="enum" /> type that has been extended by Terminal.Gui source generators.</summary>
+    public System.Type EnumType { get; init; }
+    ///<summary>A class containing extension methods for <see cref="EnumType"/>.</summary>
+    public System.Type ExtensionClass { get; init; }
+
+    /// <inheritdoc />
+    public override string ToString () => $"{EnumType.Name},{ExtensionClass.Name}";
+}

+ 22 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/CombinationGroupingAttribute.cs

@@ -0,0 +1,22 @@
+using System;
+using JetBrains.Annotations;
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>
+///     Designates an enum member for inclusion in generation of bitwise combinations with other members decorated with
+///     this attribute which have the same <see cref="GroupTag"/> value.<br/>
+/// </summary>
+/// <remarks>
+///     This attribute is only considered for members of enum types which have the
+///     <see cref="GenerateEnumExtensionMethodsAttribute"/>.
+/// </remarks>
+[AttributeUsage (AttributeTargets.Field)]
+[UsedImplicitly]
+internal sealed class CombinationGroupingAttribute : Attribute
+{
+    /// <summary>
+    ///     Name of a group this member participates in, for FastHasFlags.
+    /// </summary>
+    public string GroupTag { get; set; }
+}

+ 37 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/ExtensionsForEnumTypeAttribute.cs

@@ -0,0 +1,37 @@
+// ReSharper disable RedundantNameQualifier
+// ReSharper disable RedundantNullableDirective
+// ReSharper disable UnusedType.Global
+#pragma warning disable IDE0001, IDE0240
+#nullable enable
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>
+///     Attribute written by the source generator for <see langword="enum" /> extension classes, for easier analysis and reflection.
+/// </summary>
+/// <remarks>
+///     Properties are just convenient shortcuts to properties of <typeparamref name="TEnum"/>.
+/// </remarks>
+[System.AttributeUsage (System.AttributeTargets.Class | System.AttributeTargets.Interface)]
+internal sealed class ExtensionsForEnumTypeAttribute<TEnum>: System.Attribute, IExtensionsForEnumTypeAttributes where TEnum : struct, System.Enum
+{
+    /// <summary>
+    ///     The namespace-qualified name of <typeparamref name="TEnum"/>.
+    /// </summary>
+    public string EnumFullName => EnumType.FullName!;
+
+    /// <summary>
+    ///     The unqualified name of <typeparamref name="TEnum"/>.
+    /// </summary>
+    public string EnumName => EnumType.Name;
+
+    /// <summary>
+    ///     The namespace containing <typeparamref name="TEnum"/>.
+    /// </summary>
+    public string EnumNamespace => EnumType.Namespace!;
+
+    /// <summary>
+    ///     The <see cref="System.Type"/> given by <see langword="typeof"/>(<typeparamref name="TEnum"/>).
+    /// </summary>
+    public System.Type EnumType => typeof (TEnum);
+}

+ 110 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/GenerateEnumExtensionMethodsAttribute.cs

@@ -0,0 +1,110 @@
+// ReSharper disable RedundantNullableDirective
+// ReSharper disable RedundantUsingDirective
+// ReSharper disable ClassNeverInstantiated.Global
+
+#nullable enable
+using System;
+using Attribute = System.Attribute;
+using AttributeUsageAttribute = System.AttributeUsageAttribute;
+using AttributeTargets = System.AttributeTargets;
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>
+///     Used to enable source generation of a common set of extension methods for enum types.
+/// </summary>
+[AttributeUsage (AttributeTargets.Enum)]
+internal sealed class GenerateEnumExtensionMethodsAttribute : Attribute
+{
+    /// <summary>
+    ///     The name of the generated static class.
+    /// </summary>
+    /// <remarks>
+    ///     If unspecified, null, empty, or only whitespace, defaults to the name of the enum plus "Extensions".<br/>
+    ///     No other validation is performed, so illegal values will simply result in compiler errors.
+    ///     <para>
+    ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+    ///     </para>
+    /// </remarks>
+    public string? ClassName { get; set; }
+
+    /// <summary>
+    ///     The namespace in which to place the generated static class containing the extension methods.
+    /// </summary>
+    /// <remarks>
+    ///     If unspecified, null, empty, or only whitespace, defaults to the namespace of the enum.<br/>
+    ///     No other validation is performed, so illegal values will simply result in compiler errors.
+    ///     <para>
+    ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+    ///     </para>
+    /// </remarks>
+    public string? ClassNamespace { get; set; }
+
+    /// <summary>
+    ///     Whether to generate a fast, zero-allocation, non-boxing, and reflection-free alternative to the built-in
+    ///     <see cref="Enum.HasFlag"/> method.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Default: false
+    ///     </para>
+    ///     <para>
+    ///         If the enum is not decorated with <see cref="FlagsAttribute"/>, this option has no effect.
+    ///     </para>
+    ///     <para>
+    ///         If multiple members have the same value, the first member with that value will be used and subsequent members
+    ///         with the same value will be skipped.
+    ///     </para>
+    ///     <para>
+    ///         Overloads taking the enum type itself as well as the underlying type of the enum will be generated, enabling
+    ///         avoidance of implicit or explicit cast overhead.
+    ///     </para>
+    ///     <para>
+    ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+    ///     </para>
+    /// </remarks>
+    public bool FastHasFlags { get; set; }
+
+    /// <summary>
+    ///     Whether to generate a fast, zero-allocation, and reflection-free alternative to the built-in
+    ///     <see cref="Enum.IsDefined"/> method,
+    ///     using a switch expression as a hard-coded reverse mapping of numeric values to explicitly-named members.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Default: true
+    ///     </para>
+    ///     <para>
+    ///         If multiple members have the same value, the first member with that value will be used and subsequent members
+    ///         with the same value will be skipped.
+    ///     </para>
+    ///     <para>
+    ///         As with <see cref="Enum.IsDefined"/> the source generator only considers explicitly-named members.<br/>
+    ///         Generation of values which represent valid bitwise combinations of members of enums decorated with
+    ///         <see cref="FlagsAttribute"/> is not affected by this property.
+    ///     </para>
+    /// </remarks>
+    public bool FastIsDefined { get; init; } = true;
+
+    /// <summary>
+    ///     Gets a <see langword="bool"/> value indicating if this <see cref="GenerateEnumExtensionMethodsAttribute"/> instance
+    ///     contains default values only. See <see href="#remarks">remarks</see> of this method or documentation on properties of this type for details.
+    /// </summary>
+    /// <returns>
+    ///     A <see langword="bool"/> value indicating if all property values are default for this
+    ///     <see cref="GenerateEnumExtensionMethodsAttribute"/> instance.
+    /// </returns>
+    /// <remarks>
+    ///     Default values that will result in a <see langword="true"/> return value are:<br/>
+    ///     <see cref="FastIsDefined"/> &amp;&amp; !<see cref="FastHasFlags"/> &amp;&amp; <see cref="ClassName"/>
+    ///     <see langword="is"/> <see langword="null"/> &amp;&amp; <see cref="ClassNamespace"/> <see langword="is"/>
+    ///     <see langword="null"/>
+    /// </remarks>
+    public override bool IsDefaultAttribute ()
+    {
+        return FastIsDefined
+               && !FastHasFlags
+               && ClassName is null
+               && ClassNamespace is null;
+    }
+}

+ 110 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/GenerateEnumMemberCombinationsAttribute.cs

@@ -0,0 +1,110 @@
+// ReSharper disable RedundantUsingDirective
+
+using System;
+using JetBrains.Annotations;
+using Terminal.Gui.Analyzers.Internal.Compatibility;
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>
+///     Designates an enum member for inclusion in generation of bitwise combinations with other members decorated with
+///     this attribute which have the same <see cref="GroupTag"/> value.<br/>
+/// </summary>
+/// <remarks>
+///     <para>
+///         This attribute is only considered for enum types with the <see cref="GenerateEnumExtensionMethodsAttribute"/>.
+///     </para>
+/// </remarks>
+[AttributeUsage (AttributeTargets.Enum)]
+[UsedImplicitly]
+public sealed class GenerateEnumMemberCombinationsAttribute : System.Attribute
+{
+    private const byte MaximumPopCountLimit = 14;
+    private uint _mask;
+    private uint _maskPopCount;
+    private byte _popCountLimit = 8;
+    /// <inheritdoc cref="CombinationGroupingAttribute.GroupTag" />
+    public string GroupTag { get; set; }
+
+    /// <summary>
+    /// The mask for the group defined in <see cref="GroupTag"/>
+    /// </summary>
+    public uint Mask
+    {
+        get => _mask;
+        set
+        {
+#if NET8_0_OR_GREATER
+            _maskPopCount = uint.PopCount (value);
+#else
+            _maskPopCount = value.GetPopCount ();
+#endif
+            PopCountLimitExceeded = _maskPopCount > PopCountLimit;
+            MaximumPopCountLimitExceeded = _maskPopCount > MaximumPopCountLimit;
+
+            if (PopCountLimitExceeded || MaximumPopCountLimitExceeded)
+            {
+                return;
+            }
+
+            _mask = value;
+        }
+    }
+
+    /// <summary>
+    ///     The maximum number of bits allowed to be set to 1 in <see cref="Mask"/>.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Default: 8 (256 possible combinations)
+    ///     </para>
+    ///     <para>
+    ///         Increasing this value is not recommended!<br/>
+    ///         Decreasing this value is pointless unless you want to limit maximum possible generated combinations even
+    ///         further.
+    ///     </para>
+    ///     <para>
+    ///         If the result of <see cref="NumericExtensions.GetPopCount(uint)"/>(<see cref="Mask"/>) exceeds 2 ^
+    ///         <see cref="PopCountLimit"/>, no
+    ///         combinations will be generated for the members which otherwise would have been included by <see cref="Mask"/>.
+    ///         Values exceeding the actual population count of <see cref="Mask"/> have no effect.
+    ///     </para>
+    ///     <para>
+    ///         This option is set to a sane default of 8, but also has a hard-coded limit of 14 (16384 combinations), as a
+    ///         protection against generation of extremely large files.
+    ///     </para>
+    ///     <para>
+    ///         CAUTION: The maximum number of possible combinations possible is equal to 1 &lt;&lt;
+    ///         <see cref="NumericExtensions.GetPopCount(uint)"/>(<see cref="Mask"/>).
+    ///         See <see cref="MaximumPopCountLimit"/> for hard-coded limit,
+    ///     </para>
+    /// </remarks>
+    public byte PopCountLimit
+    {
+        get => _popCountLimit;
+        set
+        {
+#if NET8_0_OR_GREATER
+            _maskPopCount = uint.PopCount (_mask);
+#else
+            _maskPopCount = _mask.GetPopCount ();
+#endif
+
+            PopCountLimitExceeded = _maskPopCount > value;
+            MaximumPopCountLimitExceeded = _maskPopCount > MaximumPopCountLimit;
+
+            if (PopCountLimitExceeded || MaximumPopCountLimitExceeded)
+            {
+                return;
+            }
+
+            _mask = value;
+            _popCountLimit = value;
+        }
+    }
+
+    [UsedImplicitly]
+    internal bool MaximumPopCountLimitExceeded { get; private set; }
+    [UsedImplicitly]
+    internal bool PopCountLimitExceeded { get; private set; }
+}

+ 14 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/IExtensionsForEnumTypeAttribute.cs

@@ -0,0 +1,14 @@
+// ReSharper disable All
+
+using System;
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>
+///     Interface to simplify general enumeration of constructed generic types for
+///     <see cref="ExtensionsForEnumTypeAttribute{TEnum}"/>
+/// </summary>
+internal interface IExtensionsForEnumTypeAttributes
+{
+    Type EnumType { get; }
+}

+ 235 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/CodeWriter.cs

@@ -0,0 +1,235 @@
+using System;
+using System.CodeDom.Compiler;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+using Terminal.Gui.Analyzers.Internal.Constants;
+
+namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
+
+/// <summary>
+///     The class responsible for turning an <see cref="EnumExtensionMethodsGenerationInfo"/>
+///     into actual C# code.
+/// </summary>
+/// <remarks>Try to use this type as infrequently as possible.</remarks>
+/// <param name="metadata">
+///     A reference to an <see cref="IGeneratedTypeMetadata{TSelf}"/> which will be used
+///     to generate the extension class code. The object will not be validated,
+///     so it is critical that it be correct and remain unchanged while in use
+///     by an instance of this class. Behavior if those rules are not followed
+///     is undefined.
+/// </param>
+[SuppressMessage ("CodeQuality", "IDE0079", Justification = "Suppressions here are intentional and the warnings they disable are just noise.")]
+internal sealed class CodeWriter (in EnumExtensionMethodsGenerationInfo metadata) : IStandardCSharpCodeGenerator<EnumExtensionMethodsGenerationInfo>
+{
+    // Using the null suppression operator here because this will always be
+    // initialized to non-null before a reference to it is returned.
+    private SourceText _sourceText = null!;
+
+    /// <inheritdoc/>
+    public EnumExtensionMethodsGenerationInfo Metadata
+    {
+        [MethodImpl (MethodImplOptions.AggressiveInlining)]
+        [return: NotNull]
+        get;
+        [param: DisallowNull]
+        set;
+    } = metadata;
+
+    /// <inheritdoc/>
+    public ref readonly SourceText GenerateSourceText (Encoding? encoding = null)
+    {
+        encoding ??= Encoding.UTF8;
+        _sourceText = SourceText.From (GetFullSourceText (), encoding);
+
+        return ref _sourceText;
+    }
+
+    /// <summary>
+    ///     Gets the using directive for the namespace containing the enum,
+    ///     if different from the extension class namespace, or an empty string, if they are the same.
+    /// </summary>
+    private string EnumNamespaceUsingDirective => Metadata.TargetTypeNamespace != Metadata.GeneratedTypeNamespace
+
+                                                      // ReSharper disable once HeapView.ObjectAllocation
+                                                      ? $"using {Metadata.TargetTypeNamespace};"
+                                                      : string.Empty;
+
+    private string EnumTypeKeyword => Metadata.EnumBackingTypeCode switch
+                                      {
+                                          TypeCode.Int32 => "int",
+                                          TypeCode.UInt32 => "uint",
+                                          _ => string.Empty
+                                      };
+
+    /// <summary>Gets the class declaration line.</summary>
+    private string ExtensionClassDeclarationLine => $"public static partial class {Metadata.GeneratedTypeName}";
+
+    // ReSharper disable once HeapView.ObjectAllocation
+    /// <summary>Gets the XmlDoc for the extension class declaration.</summary>
+    private string ExtensionClassDeclarationXmlDoc =>
+        $"/// <summary>Extension methods for the <see cref=\"{Metadata.TargetTypeFullName}\"/> <see langword=\"enum\" /> type.</summary>";
+
+    // ReSharper disable once HeapView.ObjectAllocation
+    /// <summary>Gets the extension class file-scoped namespace directive.</summary>
+    private string ExtensionClassNamespaceDirective => $"namespace {Metadata.GeneratedTypeNamespace};";
+
+    /// <summary>
+    ///     An attribute to decorate the extension class with for easy mapping back to the target enum type, for reflection and
+    ///     analysis.
+    /// </summary>
+    private string ExtensionsForTypeAttributeLine => $"[ExtensionsForEnumType<{Metadata.TargetTypeFullName}>]";
+
+    /// <summary>
+    ///     Creates the code for the FastHasFlags method.
+    /// </summary>
+    /// <remarks>
+    ///     Since the generator already only writes code for enums backed by <see langword="int"/> and <see langword="uint"/>,
+    ///     this method is safe, as we'll always be using a DWORD.
+    /// </remarks>
+    /// <param name="w">An instance of an <see cref="IndentedTextWriter"/> to write to.</param>
+    private void GetFastHasFlagsMethods (IndentedTextWriter w)
+    {
+        // The version taking the same enum type as the check value.
+        w.WriteLine (
+                     $"/// <summary>Determines if the specified flags are set in the current value of this <see cref=\"{Metadata.TargetTypeFullName}\" />.</summary>");
+        w.WriteLine ("/// <remarks>NO VALIDATION IS PERFORMED!</remarks>");
+
+        w.WriteLine (
+                     $"/// <returns>True, if all flags present in <paramref name=\"checkFlags\" /> are also present in the current value of the <see cref=\"{Metadata.TargetTypeFullName}\" />.<br />Otherwise false.</returns>");
+        w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining);
+
+        w.Push (
+                $"{Metadata.Accessibility.ToCSharpString ()} static bool FastHasFlags (this {Metadata.TargetTypeFullName} e, {Metadata.TargetTypeFullName} checkFlags)");
+        w.WriteLine ($"ref uint enumCurrentValueRef = ref Unsafe.As<{Metadata.TargetTypeFullName},uint> (ref e);");
+        w.WriteLine ($"ref uint checkFlagsValueRef = ref Unsafe.As<{Metadata.TargetTypeFullName},uint> (ref checkFlags);");
+        w.WriteLine ("return (enumCurrentValueRef & checkFlagsValueRef) == checkFlagsValueRef;");
+        w.Pop ();
+
+        // The version taking the underlying type of the enum as the check value.
+        w.WriteLine (
+                     $"/// <summary>Determines if the specified mask bits are set in the current value of this <see cref=\"{Metadata.TargetTypeFullName}\" />.</summary>");
+
+        w.WriteLine (
+                     $"/// <param name=\"e\">The <see cref=\"{Metadata.TargetTypeFullName}\" /> value to check against the <paramref name=\"mask\" /> value.</param>");
+        w.WriteLine ("/// <param name=\"mask\">A mask to apply to the current value.</param>");
+
+        w.WriteLine (
+                     $"/// <returns>True, if all bits set to 1 in the mask are also set to 1 in the current value of the <see cref=\"{Metadata.TargetTypeFullName}\" />.<br />Otherwise false.</returns>");
+        w.WriteLine ("/// <remarks>NO VALIDATION IS PERFORMED!</remarks>");
+        w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining);
+
+        w.Push (
+                $"{Metadata.Accessibility.ToCSharpString ()} static bool FastHasFlags (this {Metadata.TargetTypeFullName} e, {EnumTypeKeyword} mask)");
+        w.WriteLine ($"ref {EnumTypeKeyword} enumCurrentValueRef = ref Unsafe.As<{Metadata.TargetTypeFullName},{EnumTypeKeyword}> (ref e);");
+        w.WriteLine ("return (enumCurrentValueRef & mask) == mask;");
+        w.Pop ();
+    }
+
+    /// <summary>
+    ///     Creates the code for the FastIsDefined method.
+    /// </summary>
+    [SuppressMessage ("ReSharper", "SwitchStatementHandlesSomeKnownEnumValuesWithDefault", Justification = "Only need to handle int and uint.")]
+    [SuppressMessage ("ReSharper", "SwitchStatementMissingSomeEnumCasesNoDefault", Justification = "Only need to handle int and uint.")]
+    private void GetFastIsDefinedMethod (IndentedTextWriter w)
+    {
+        w.WriteLine (
+                     $"/// <summary>Determines if the specified <see langword=\"{EnumTypeKeyword}\" /> value is explicitly defined as a named value of the <see cref=\"{Metadata.TargetTypeFullName}\" /> <see langword=\"enum\" /> type.</summary>");
+
+        w.WriteLine (
+                     "/// <remarks>Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are not explicitly named will return false.</remarks>");
+
+        w.Push (
+                $"{Metadata.Accessibility.ToCSharpString ()} static bool FastIsDefined (this {Metadata.TargetTypeFullName} e, {EnumTypeKeyword} value)");
+        w.Push ("return value switch");
+
+        switch (Metadata.EnumBackingTypeCode)
+        {
+            case TypeCode.Int32:
+                foreach (int definedValue in Metadata.IntMembers)
+                {
+                    w.WriteLine ($"{definedValue:D} => true,");
+                }
+
+                break;
+            case TypeCode.UInt32:
+                foreach (uint definedValue in Metadata.UIntMembers)
+                {
+                    w.WriteLine ($"{definedValue:D} => true,");
+                }
+
+                break;
+        }
+
+        w.WriteLine ("_ => false");
+
+        w.Pop ("};");
+        w.Pop ();
+    }
+
+    private string GetFullSourceText ()
+    {
+        StringBuilder sb = new (
+                                $"""
+                                 {Strings.Templates.StandardHeader}
+
+                                 [assembly: {Strings.AssemblyExtendedEnumTypeAttributeFullName} (typeof({Metadata.TargetTypeFullName}), typeof({Metadata.GeneratedTypeFullName}))]
+
+                                 {EnumNamespaceUsingDirective}
+                                 {ExtensionClassNamespaceDirective}
+                                 {ExtensionClassDeclarationXmlDoc}
+                                 {Strings.Templates.AttributesForGeneratedTypes}
+                                 {ExtensionsForTypeAttributeLine}
+                                 {ExtensionClassDeclarationLine}
+                                 
+                                 """,
+                                4096);
+
+        using IndentedTextWriter w = new (new StringWriter (sb));
+        w.Push ();
+
+        GetNamedValuesToInt32Method (w);
+        GetNamedValuesToUInt32Method (w);
+
+        if (Metadata.GenerateFastIsDefined)
+        {
+            GetFastIsDefinedMethod (w);
+        }
+
+        if (Metadata.GenerateFastHasFlags)
+        {
+            GetFastHasFlagsMethods (w);
+        }
+
+        w.Pop ();
+
+        w.Flush ();
+
+        return sb.ToString ();
+    }
+
+    [MethodImpl (MethodImplOptions.AggressiveInlining)]
+    private void GetNamedValuesToInt32Method (IndentedTextWriter w)
+    {
+        w.WriteLine (
+                     $"/// <summary>Directly converts this <see cref=\"{Metadata.TargetTypeFullName}\" /> value to an <see langword=\"int\" /> value with the same binary representation.</summary>");
+        w.WriteLine ("/// <remarks>NO VALIDATION IS PERFORMED!</remarks>");
+        w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining);
+        w.Push ($"{Metadata.Accessibility.ToCSharpString ()} static int AsInt32 (this {Metadata.TargetTypeFullName} e)");
+        w.WriteLine ($"return Unsafe.As<{Metadata.TargetTypeFullName},int> (ref e);");
+        w.Pop ();
+    }
+
+    [MethodImpl (MethodImplOptions.AggressiveInlining)]
+    private void GetNamedValuesToUInt32Method (IndentedTextWriter w)
+    {
+        w.WriteLine (
+                     $"/// <summary>Directly converts this <see cref=\"{Metadata.TargetTypeFullName}\" /> value to a <see langword=\"uint\" /> value with the same binary representation.</summary>");
+        w.WriteLine ("/// <remarks>NO VALIDATION IS PERFORMED!</remarks>");
+        w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining);
+        w.Push ($"{Metadata.Accessibility.ToCSharpString ()} static uint AsUInt32 (this {Metadata.TargetTypeFullName} e)");
+        w.WriteLine ($"return Unsafe.As<{Metadata.TargetTypeFullName},uint> (ref e);");
+        w.Pop ();
+    }
+}

+ 457 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsGenerationInfo.cs

@@ -0,0 +1,457 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Threading;
+using JetBrains.Annotations;
+using Microsoft.CodeAnalysis;
+using Terminal.Gui.Analyzers.Internal.Attributes;
+using Terminal.Gui.Analyzers.Internal.Constants;
+
+namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
+
+/// <summary>
+///     Type containing the information necessary to generate code according to the declared attribute values,
+///     as well as the actual code to create the corresponding source code text, to be used in the
+///     source generator pipeline.
+/// </summary>
+/// <remarks>
+///     Minimal validation is performed by this type.<br/>
+///     Errors in analyzed source code will result in generation failure or broken output.<br/>
+///     This type is not intended for use outside of Terminal.Gui library development.
+/// </remarks>
+internal sealed record EnumExtensionMethodsGenerationInfo : IGeneratedTypeMetadata<EnumExtensionMethodsGenerationInfo>,
+                                                            IEqualityOperators<EnumExtensionMethodsGenerationInfo, EnumExtensionMethodsGenerationInfo, bool>
+{
+    private const int ExplicitFastHasFlagsMask = 0b1000;
+    private const int ExplicitFastIsDefinedMask = 0b1_0000;
+    private const int ExplicitIncludeInterfaceMask = 0b10_0000;
+    private const int ExplicitNameMask = 0b10;
+    private const int ExplicitNamespaceMask = 0b1;
+    private const int ExplicitPartialMask = 0b100;
+    private const string GeneratorAttributeFullyQualifiedName = $"{GeneratorAttributeNamespace}.{GeneratorAttributeName}";
+    private const string GeneratorAttributeName = nameof (GenerateEnumExtensionMethodsAttribute);
+    private const string GeneratorAttributeNamespace = Constants.Strings.AnalyzersAttributesNamespace;
+
+    /// <summary>
+    ///     Type containing the information necessary to generate code according to the declared attribute values,
+    ///     as well as the actual code to create the corresponding source code text, to be used in the
+    ///     source generator pipeline.
+    /// </summary>
+    /// <param name="enumNamespace">The fully-qualified namespace of the enum type, without assembly name.</param>
+    /// <param name="enumTypeName">
+    ///     The name of the enum type, as would be given by <see langword="nameof"/> on the enum's type
+    ///     declaration.
+    /// </param>
+    /// <param name="typeNamespace">
+    ///     The fully-qualified namespace in which to place the generated code, without assembly name. If omitted or explicitly
+    ///     null, uses the value provided in <paramref name="enumNamespace"/>.
+    /// </param>
+    /// <param name="typeName">
+    ///     The name of the generated class. If omitted or explicitly null, appends "Extensions" to the value of
+    ///     <paramref name="enumTypeName"/>.
+    /// </param>
+    /// <param name="enumBackingTypeCode">The backing type of the enum. Defaults to <see cref="int"/>.</param>
+    /// <param name="generateFastHasFlags">
+    ///     Whether to generate a fast HasFlag alternative. (Default: true) Ignored if the enum does not also have
+    ///     <see cref="FlagsAttribute"/>.
+    /// </param>
+    /// <param name="generateFastIsDefined">Whether to generate a fast IsDefined alternative. (Default: true)</param>
+    /// <remarks>
+    ///     Minimal validation is performed by this type.<br/>
+    ///     Errors in analyzed source code will result in generation failure or broken output.<br/>
+    ///     This type is not intended for use outside of Terminal.Gui library development.
+    /// </remarks>
+    public EnumExtensionMethodsGenerationInfo (
+        string enumNamespace,
+        string enumTypeName,
+        string? typeNamespace = null,
+        string? typeName = null,
+        TypeCode enumBackingTypeCode = TypeCode.Int32,
+        bool generateFastHasFlags = true,
+        bool generateFastIsDefined = true
+    ) : this (enumNamespace, enumTypeName, enumBackingTypeCode)
+    {
+        GeneratedTypeNamespace = typeNamespace ?? enumNamespace;
+        GeneratedTypeName = typeName ?? string.Concat (enumTypeName, Strings.DefaultTypeNameSuffix);
+        GenerateFastHasFlags = generateFastHasFlags;
+        GenerateFastIsDefined = generateFastIsDefined;
+    }
+
+    public EnumExtensionMethodsGenerationInfo (string enumNamespace, string enumTypeName, TypeCode enumBackingType)
+    {
+        // Interning these since they're rather unlikely to change.
+        string enumInternedNamespace = string.Intern (enumNamespace);
+        string enumInternedName = string.Intern (enumTypeName);
+        TargetTypeNamespace = enumInternedNamespace;
+        TargetTypeName = enumInternedName;
+        EnumBackingTypeCode = enumBackingType;
+    }
+
+    [AccessedThroughProperty (nameof (EnumBackingTypeCode))]
+    private TypeCode _enumBackingTypeCode;
+
+    [AccessedThroughProperty (nameof (GeneratedTypeName))]
+    private string? _generatedTypeName;
+
+    [AccessedThroughProperty (nameof (GeneratedTypeNamespace))]
+    private string? _generatedTypeNamespace;
+
+    private BitVector32 _discoveredProperties = new (0);
+
+    /// <summary>The name of the extension class.</summary>
+    public string? GeneratedTypeName
+    {
+        get => _generatedTypeName ?? string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix);
+        set => _generatedTypeName = value ?? string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix);
+    }
+
+    /// <summary>The namespace for the extension class.</summary>
+    /// <remarks>
+    ///     Value is not validated by the set accessor.<br/>
+    ///     Get accessor will never return null and is thus marked [NotNull] for static analysis, even though the property is
+    ///     declared as a nullable <see langword="string?"/>.<br/>If the backing field for this property is null, the get
+    ///     accessor will return <see cref="TargetTypeNamespace"/> instead.
+    /// </remarks>
+    public string? GeneratedTypeNamespace
+    {
+        get => _generatedTypeNamespace ?? TargetTypeNamespace;
+        set => _generatedTypeNamespace = value ?? TargetTypeNamespace;
+    }
+
+    /// <inheritdoc/>
+    public string TargetTypeFullName => string.Concat (TargetTypeNamespace, ".", TargetTypeName);
+
+    /// <inheritdoc/>
+    public Accessibility Accessibility
+    {
+        get;
+        [UsedImplicitly]
+        internal set;
+    } = Accessibility.Public;
+
+    /// <inheritdoc/>
+    public TypeKind TypeKind => TypeKind.Class;
+
+    /// <inheritdoc/>
+    public bool IsRecord => false;
+
+    /// <inheritdoc/>
+    public bool IsClass => true;
+
+    /// <inheritdoc/>
+    public bool IsStruct => false;
+
+    /// <inheritdoc/>
+    public bool IsByRefLike => false;
+
+    /// <inheritdoc/>
+    public bool IsSealed => false;
+
+    /// <inheritdoc/>
+    public bool IsAbstract => false;
+
+    /// <inheritdoc/>
+    public bool IsEnum => false;
+
+    /// <inheritdoc/>
+    public bool IsStatic => true;
+
+    /// <inheritdoc/>
+    public bool IncludeInterface { get; private set; }
+
+    public string GeneratedTypeFullName => $"{GeneratedTypeNamespace}.{GeneratedTypeName}";
+
+    /// <summary>Whether to generate the extension class as partial (Default: true)</summary>
+    public bool IsPartial => true;
+
+    /// <summary>The fully-qualified namespace of the source enum type.</summary>
+    public string TargetTypeNamespace
+    {
+        get;
+        [UsedImplicitly]
+        set;
+    }
+
+    /// <summary>The UNQUALIFIED name of the source enum type.</summary>
+    public string TargetTypeName
+    {
+        get;
+        [UsedImplicitly]
+        set;
+    }
+
+    /// <summary>
+    ///     The backing type for the enum.
+    /// </summary>
+    /// <remarks>For simplicity and formality, only System.Int32 and System.UInt32 are supported at this time.</remarks>
+    public TypeCode EnumBackingTypeCode
+    {
+        get => _enumBackingTypeCode;
+        set
+        {
+            if (value is not TypeCode.Int32 and not TypeCode.UInt32)
+            {
+                throw new NotSupportedException ("Only System.Int32 and System.UInt32 are supported at this time.");
+            }
+
+            _enumBackingTypeCode = value;
+        }
+    }
+
+    /// <summary>
+    ///     Whether a fast alternative to the built-in Enum.HasFlag method will be generated (Default: false)
+    /// </summary>
+    public bool GenerateFastHasFlags { [UsedImplicitly] get; set; }
+
+    /// <summary>Whether a switch-based IsDefined replacement will be generated (Default: true)</summary>
+    public bool GenerateFastIsDefined { [UsedImplicitly]get; set; } = true;
+
+    internal ImmutableHashSet<int>? IntMembers;
+    internal ImmutableHashSet<uint>? UIntMembers;
+
+    /// <summary>
+    ///     Fully-qualified name of the extension class
+    /// </summary>
+    internal string FullyQualifiedClassName => $"{GeneratedTypeNamespace}.{GeneratedTypeName}";
+
+    /// <summary>
+    ///     Whether a Flags was found on the enum type.
+    /// </summary>
+    internal bool HasFlagsAttribute {[UsedImplicitly] get; set; }
+
+    private static readonly SymbolDisplayFormat FullyQualifiedSymbolDisplayFormatWithoutGlobal =
+        SymbolDisplayFormat.FullyQualifiedFormat
+                           .WithGlobalNamespaceStyle (
+                                                      SymbolDisplayGlobalNamespaceStyle.Omitted);
+
+
+    internal bool TryConfigure (INamedTypeSymbol enumSymbol, CancellationToken cancellationToken)
+    {
+        using var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken);
+        cts.Token.ThrowIfCancellationRequested ();
+
+        ImmutableArray<AttributeData> attributes = enumSymbol.GetAttributes ();
+
+        // This is theoretically impossible, but guarding just in case and canceling if it does happen.
+        if (attributes.Length == 0)
+        {
+            cts.Cancel (true);
+
+            return false;
+        }
+
+        // Check all attributes provided for anything interesting.
+        // Attributes can be in any order, so just check them all and adjust at the end if necessary.
+        // Note that we do not perform as strict validation on actual usage of the attribute, at this stage,
+        // because the analyzer should have already thrown errors for invalid uses like global namespace
+        // or unsupported enum underlying types.
+        foreach (AttributeData attr in attributes)
+        {
+            cts.Token.ThrowIfCancellationRequested ();
+            string? attributeFullyQualifiedName = attr.AttributeClass?.ToDisplayString (FullyQualifiedSymbolDisplayFormatWithoutGlobal);
+
+            // Skip if null or not possibly an attribute we care about
+            if (attributeFullyQualifiedName is null or not { Length: >= 5 })
+            {
+                continue;
+            }
+
+            switch (attributeFullyQualifiedName)
+            {
+                // For Flags enums
+                case Strings.DotnetNames.Attributes.Flags:
+                {
+                    HasFlagsAttribute = true;
+                }
+
+                    continue;
+
+                // For the attribute that started this whole thing
+                case GeneratorAttributeFullyQualifiedName:
+
+                {
+                    // If we can't successfully complete this method,
+                    // something is wrong enough that we may as well just stop now.
+                    if (!TryConfigure (attr, cts.Token))
+                    {
+                        if (cts.Token.CanBeCanceled)
+                        {
+                            cts.Cancel ();
+                        }
+
+                        return false;
+                    }
+                }
+
+                    continue;
+            }
+        }
+
+        // Now get the members, if we know we'll need them.
+        if (GenerateFastIsDefined || GenerateFastHasFlags)
+        {
+            if (EnumBackingTypeCode == TypeCode.Int32)
+            {
+                PopulateIntMembersHashSet (enumSymbol);
+            }
+            else if (EnumBackingTypeCode == TypeCode.UInt32)
+            {
+                PopulateUIntMembersHashSet (enumSymbol);
+            }
+        }
+
+        return true;
+    }
+
+    private void PopulateIntMembersHashSet (INamedTypeSymbol enumSymbol)
+    {
+        ImmutableArray<ISymbol> enumMembers = enumSymbol.GetMembers ();
+        IEnumerable<IFieldSymbol> fieldSymbols = enumMembers.OfType<IFieldSymbol> ();
+        IntMembers = fieldSymbols.Select (static m => m.HasConstantValue ? (int)m.ConstantValue : 0).ToImmutableHashSet ();
+    }
+    private void PopulateUIntMembersHashSet (INamedTypeSymbol enumSymbol)
+    {
+        UIntMembers = enumSymbol.GetMembers ().OfType<IFieldSymbol> ().Select (static m => (uint)m.ConstantValue).ToImmutableHashSet ();
+    }
+
+    private bool HasExplicitFastHasFlags
+    {
+        [UsedImplicitly]get => _discoveredProperties [ExplicitFastHasFlagsMask];
+        set => _discoveredProperties [ExplicitFastHasFlagsMask] = value;
+    }
+
+    private bool HasExplicitFastIsDefined
+    {
+        [UsedImplicitly]get => _discoveredProperties [ExplicitFastIsDefinedMask];
+        set => _discoveredProperties [ExplicitFastIsDefinedMask] = value;
+    }
+
+    private bool HasExplicitIncludeInterface
+    {
+        [UsedImplicitly]get => _discoveredProperties [ExplicitIncludeInterfaceMask];
+        set => _discoveredProperties [ExplicitIncludeInterfaceMask] = value;
+    }
+
+    private bool HasExplicitPartial
+    {
+        [UsedImplicitly]get => _discoveredProperties [ExplicitPartialMask];
+        set => _discoveredProperties [ExplicitPartialMask] = value;
+    }
+
+    private bool HasExplicitTypeName
+    {
+        get => _discoveredProperties [ExplicitNameMask];
+        set => _discoveredProperties [ExplicitNameMask] = value;
+    }
+
+    private bool HasExplicitTypeNamespace
+    {
+        get => _discoveredProperties [ExplicitNamespaceMask];
+        set => _discoveredProperties [ExplicitNamespaceMask] = value;
+    }
+
+    [MemberNotNullWhen (true, nameof (_generatedTypeName), nameof (_generatedTypeNamespace))]
+    private bool TryConfigure (AttributeData attr, CancellationToken cancellationToken)
+    {
+        using var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken);
+        cts.Token.ThrowIfCancellationRequested ();
+
+        if (attr is not { NamedArguments.Length: > 0 })
+        {
+            // Just a naked attribute, so configure with appropriate defaults.
+            GeneratedTypeNamespace = TargetTypeNamespace;
+            GeneratedTypeName = string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix);
+
+            return true;
+        }
+
+        cts.Token.ThrowIfCancellationRequested ();
+
+        foreach (KeyValuePair<string, TypedConstant> kvp in attr.NamedArguments)
+        {
+            string? propName = kvp.Key;
+            TypedConstant propValue = kvp.Value;
+
+            cts.Token.ThrowIfCancellationRequested ();
+
+            // For every property name and value pair, set associated metadata
+            // property, if understood.
+            switch (propName, propValue)
+            {
+                // Null or empty string doesn't make sense, so skip if it happens.
+                case (null, _):
+                case ("", _):
+                    continue;
+
+                // ClassName is specified, not explicitly null, and at least 1 character long.
+                case (AttributeProperties.TypeNamePropertyName, { IsNull: false, Value: string { Length: > 1 } classNameProvidedValue }):
+                    if (string.IsNullOrWhiteSpace (classNameProvidedValue))
+                    {
+                        return false;
+                    }
+
+                    GeneratedTypeName = classNameProvidedValue;
+                    HasExplicitTypeName = true;
+
+                    continue;
+
+                // Class namespace is specified, not explicitly null, and at least 1 character long.
+                case (AttributeProperties.TypeNamespacePropertyName, { IsNull: false, Value: string { Length: > 1 } classNamespaceProvidedValue }):
+
+                    if (string.IsNullOrWhiteSpace (classNamespaceProvidedValue))
+                    {
+                        return false;
+                    }
+
+                    GeneratedTypeNamespace = classNamespaceProvidedValue;
+                    HasExplicitTypeNamespace = true;
+
+                    continue;
+
+                // FastHasFlags is specified
+                case (AttributeProperties.FastHasFlagsPropertyName, { IsNull: false } fastHasFlagsConstant):
+                    GenerateFastHasFlags = fastHasFlagsConstant.Value is true;
+                    HasExplicitFastHasFlags = true;
+
+                    continue;
+
+                // FastIsDefined is specified
+                case (AttributeProperties.FastIsDefinedPropertyName, { IsNull: false } fastIsDefinedConstant):
+                    GenerateFastIsDefined = fastIsDefinedConstant.Value is true;
+                    HasExplicitFastIsDefined = true;
+
+                    continue;
+            }
+        }
+
+        // The rest is simple enough it's not really worth worrying about cancellation, so don't bother from here on...
+
+        // Configure anything that wasn't specified that doesn't have an implicitly safe default
+        if (!HasExplicitTypeName || _generatedTypeName is null)
+        {
+            _generatedTypeName = string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix);
+        }
+
+        if (!HasExplicitTypeNamespace || _generatedTypeNamespace is null)
+        {
+            _generatedTypeNamespace = TargetTypeNamespace;
+        }
+
+        if (!HasFlagsAttribute)
+        {
+            GenerateFastHasFlags = false;
+        }
+
+        return true;
+    }
+
+    private static class AttributeProperties
+    {
+        internal const string FastHasFlagsPropertyName = nameof (GenerateEnumExtensionMethodsAttribute.FastHasFlags);
+        internal const string FastIsDefinedPropertyName = nameof (GenerateEnumExtensionMethodsAttribute.FastIsDefined);
+        internal const string TypeNamePropertyName = nameof (GenerateEnumExtensionMethodsAttribute.ClassName);
+        internal const string TypeNamespacePropertyName = nameof (GenerateEnumExtensionMethodsAttribute.ClassNamespace);
+    }
+}

+ 452 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGenerator.cs

@@ -0,0 +1,452 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Text;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Terminal.Gui.Analyzers.Internal.Attributes;
+using Terminal.Gui.Analyzers.Internal.Constants;
+
+namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
+
+/// <summary>
+///     Incremental code generator for enums decorated with <see cref="GenerateEnumExtensionMethodsAttribute"/>.
+/// </summary>
+[SuppressMessage ("CodeQuality", "IDE0079", Justification = "Suppressions here are intentional and the warnings they disable are just noise.")]
+[Generator (LanguageNames.CSharp)]
+public sealed class EnumExtensionMethodsIncrementalGenerator : IIncrementalGenerator
+{
+    private const string ExtensionsForEnumTypeAttributeFullyQualifiedName = $"{Strings.AnalyzersAttributesNamespace}.{ExtensionsForEnumTypeAttributeName}";
+    private const string ExtensionsForEnumTypeAttributeName = "ExtensionsForEnumTypeAttribute";
+    private const string GeneratorAttributeFullyQualifiedName = $"{Strings.AnalyzersAttributesNamespace}.{GeneratorAttributeName}";
+    private const string GeneratorAttributeName = nameof (GenerateEnumExtensionMethodsAttribute);
+
+    /// <summary>Fully-qualified symbol name format without the "global::" prefix.</summary>
+    private static readonly SymbolDisplayFormat FullyQualifiedSymbolDisplayFormatWithoutGlobal =
+        SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle (SymbolDisplayGlobalNamespaceStyle.Omitted);
+
+    /// <inheritdoc/>
+    /// <remarks>
+    ///     <para>
+    ///         Basically, this method is called once by the compiler, and is responsible for wiring up
+    ///         everything important about how source generation works.
+    ///     </para>
+    ///     <para>
+    ///         See in-line comments for specifics of what's going on.
+    ///     </para>
+    ///     <para>
+    ///         Note that <paramref name="context"/> is everything in the compilation,
+    ///         except for code generated by this generator or generators which have not yet executed.<br/>
+    ///         The methods registered to perform generation get called on-demand by the host (the IDE,
+    ///         compiler, etc), sometimes as often as every single keystroke.
+    ///     </para>
+    /// </remarks>
+    public void Initialize (IncrementalGeneratorInitializationContext context)
+    {
+        // Write out namespaces that may be used later. Harmless to declare them now and will avoid
+        // additional processing and potential omissions later on.
+        context.RegisterPostInitializationOutput (GenerateDummyNamespaces);
+
+        // This executes the delegate given to it immediately after Roslyn gets all set up.
+        // 
+        // As written, this will result in the GenerateEnumExtensionMethodsAttribute code
+        // being added to the environment, so that it can be used without having to actually
+        // be declared explicitly in the target project.
+        // This is important, as it guarantees the type will exist and also guarantees it is
+        // defined exactly as the generator expects it to be defined.
+        context.RegisterPostInitializationOutput (GenerateAttributeSources);
+
+        // Next up, we define our pipeline.
+        // To do so, we create one or more IncrementalValuesProvider<T> objects, each of which
+        // defines on stage of analysis or generation as needed.
+        //
+        // Critically, these must be as fast and efficient as reasonably possible because,
+        // once the pipeline is registered, this stuff can get called A LOT.
+        //
+        // Note that declaring these doesn't really do much of anything unless they are given to the
+        // RegisterSourceOutput method at the end of this method.
+        //
+        // The delegates are not actually evaluated right here. That is triggered by changes being
+        // made to the source code.
+
+        // This provider grabs attributes that pass our filter and then creates lightweight
+        // metadata objects to be used in the final code generation step.
+        // It also preemptively removes any nulls from the collection before handing things off
+        // to the code generation logic.
+        IncrementalValuesProvider<EnumExtensionMethodsGenerationInfo?> enumGenerationInfos =
+            context
+                .SyntaxProvider
+
+                // This method is a highly-optimized (and highly-recommended) filter on the incoming
+                // code elements that only bothers to present code that is annotated with the specified
+                // attribute, by its fully-qualified name, as a string, which is the first parameter.
+                //
+                // Two delegates are passed to it, in the second and third parameters.
+                //
+                // The second parameter is a filter predicate taking each SyntaxNode that passes the
+                // name filter above, and then refines that result.
+                //
+                // It is critical that the filter predicate be as simple and fast as possible, as it
+                // will be called a ton, triggered by keystrokes or anything else that modifies code
+                // in or even related to (in either direction) the pre-filtered code.
+                // It should collect metadata only and not actually generate any code.
+                // It must return a boolean indicating whether the supplied SyntaxNode should be
+                // given to the transform delegate at all.
+                // 
+                // The third parameter is the "transform" delegate.
+                // That one only runs when code is changed that passed both the attribute name filter
+                // and the filter predicate in the second parameter.
+                // It will be called for everything that passes both of those, so it can still happen
+                // a lot, but should at least be pretty close.
+                // In our case, it should be 100% accurate, since we're using OUR attribute, which can
+                // only be applied to enum types in the first place.
+                //
+                // That delegate is responsible for creating some sort of lightweight data structure
+                // which can later be used to generate the actual source code for output.
+                //
+                // THIS DELEGATE DOES NOT GENERATE CODE!
+                // However, it does need to return instances of the metadata class in use that are either
+                // null or complete enough to generate meaningful code from, later on.
+                //
+                // We then filter out any that were null with the .Where call at the end, so that we don't
+                // know or care about them when it's time to generate code.
+                //
+                // While the syntax of that .Where call is the same as LINQ, that is actually a
+                // highly-optimized implementation specifically for this use.
+                .ForAttributeWithMetadataName (
+                                               GeneratorAttributeFullyQualifiedName,
+                                               IsPotentiallyInterestingDeclaration,
+                                               GatherMetadataForCodeGeneration
+                                              )
+                .WithTrackingName ("CollectEnumMetadata")
+                .Where (static eInfo => eInfo is { });
+
+        // Finally, we wire up any IncrementalValuesProvider<T> instances above to the appropriate
+        // delegate that takes the SourceProductionContext that is current at run-time and an instance of
+        // our metadata type and takes appropriate action.
+        // Typically that means generating code from that metadata and adding it to the compilation via
+        // the received context object.
+        //
+        // As with everything else , the delegate will be invoked once for each item that passed
+        // all of the filters above, so we get to write that method from the perspective of a single
+        // enum type declaration.
+
+        context.RegisterSourceOutput (enumGenerationInfos, GenerateSourceFromGenerationInfo);
+    }
+
+    private static EnumExtensionMethodsGenerationInfo? GatherMetadataForCodeGeneration (
+        GeneratorAttributeSyntaxContext context,
+        CancellationToken cancellationToken
+    )
+    {
+        var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken);
+        cancellationToken.ThrowIfCancellationRequested ();
+
+        // If it's not an enum symbol, we don't care.
+        // EnumUnderlyingType is null for non-enums, so this validates it's an enum declaration.
+        if (context.TargetSymbol is not INamedTypeSymbol { EnumUnderlyingType: { } } namedSymbol)
+        {
+            return null;
+        }
+
+        INamespaceSymbol? enumNamespaceSymbol = namedSymbol.ContainingNamespace;
+
+        if (enumNamespaceSymbol is null or { IsGlobalNamespace: true })
+        {
+            // Explicitly choosing not to support enums in the global namespace.
+            // The corresponding analyzer will report this.
+            return null;
+        }
+
+        string enumName = namedSymbol.Name;
+
+        string enumNamespace = enumNamespaceSymbol.ToDisplayString (FullyQualifiedSymbolDisplayFormatWithoutGlobal);
+
+        TypeCode enumTypeCode = namedSymbol.EnumUnderlyingType.Name switch
+                                {
+                                    "UInt32" => TypeCode.UInt32,
+                                    "Int32" => TypeCode.Int32,
+                                    _ => TypeCode.Empty
+                                };
+
+        EnumExtensionMethodsGenerationInfo info = new (
+                                                       enumNamespace,
+                                                       enumName,
+                                                       enumTypeCode
+                                                      );
+
+        if (!info.TryConfigure (namedSymbol, cts.Token))
+        {
+            cts.Cancel ();
+            cts.Token.ThrowIfCancellationRequested ();
+        }
+
+        return info;
+    }
+
+
+    private static void GenerateAttributeSources (IncrementalGeneratorPostInitializationContext postInitializationContext)
+    {
+        postInitializationContext
+            .AddSource (
+                        $"{nameof (IExtensionsForEnumTypeAttributes)}.g.cs",
+                        SourceText.From (
+                                         $$"""
+                                           // ReSharper disable All
+                                           {{Strings.Templates.AutoGeneratedCommentBlock}}
+                                           using System;
+
+                                           namespace {{Strings.AnalyzersAttributesNamespace}};
+
+                                           /// <summary>
+                                           ///     Interface to simplify general enumeration of constructed generic types for
+                                           ///     <see cref="ExtensionsForEnumTypeAttribute{TEnum}"/>
+                                           /// </summary>
+                                           {{Strings.Templates.AttributesForGeneratedInterfaces}}
+                                           public interface IExtensionsForEnumTypeAttributes
+                                           {
+                                               System.Type EnumType { get; }
+                                           }
+
+                                           """,
+                                         Encoding.UTF8));
+
+        postInitializationContext
+            .AddSource (
+                        $"{nameof (AssemblyExtendedEnumTypeAttribute)}.g.cs",
+                        SourceText.From (
+                                         $$"""
+                                           // ReSharper disable All
+                                           #nullable enable
+                                           {{Strings.Templates.AutoGeneratedCommentBlock}}
+
+                                           namespace {{Strings.AnalyzersAttributesNamespace}};
+
+                                           /// <summary>Assembly attribute declaring a known pairing of an <see langword="enum" /> type to an extension class.</summary>
+                                           /// <remarks>This attribute should only be written by internal source generators for Terminal.Gui. No other usage of any kind is supported.</remarks>
+                                           {{Strings.Templates.AttributesForGeneratedTypes}}
+                                           [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple = true)]
+                                           public sealed class {{nameof(AssemblyExtendedEnumTypeAttribute)}} : System.Attribute
+                                           {
+                                               /// <summary>Creates a new instance of <see cref="AssemblyExtendedEnumTypeAttribute" /> from the provided parameters.</summary>
+                                               /// <param name="enumType">The <see cref="System.Type" /> of an <see langword="enum" /> decorated with a <see cref="GenerateEnumExtensionMethodsAttribute" />.</param>
+                                               /// <param name="extensionClass">The <see cref="System.Type" /> of the <see langword="class" /> decorated with an <see cref="ExtensionsForEnumTypeAttribute{TEnum}" /> referring to the same type as <paramref name="enumType" />.</param>
+                                               public AssemblyExtendedEnumTypeAttribute (System.Type enumType, System.Type extensionClass)
+                                               {
+                                                   EnumType = enumType;
+                                                   ExtensionClass = extensionClass;
+                                               }
+                                               /// <summary>An <see langword="enum" /> type that has been extended by Terminal.Gui source generators.</summary>
+                                               public System.Type EnumType { get; init; }
+                                               /// <summary>A class containing extension methods for <see cref="EnumType"/>.</summary>
+                                               public System.Type ExtensionClass { get; init; }
+                                               /// <inheritdoc />
+                                               public override string ToString () => $"{EnumType.Name},{ExtensionClass.Name}";
+                                           }
+
+                                           """,
+                                         Encoding.UTF8));
+
+        postInitializationContext
+            .AddSource (
+                        $"{GeneratorAttributeFullyQualifiedName}.g.cs",
+                        SourceText.From (
+                                         $$"""
+                                           {{Strings.Templates.StandardHeader}}
+                                           
+                                           namespace {{Strings.AnalyzersAttributesNamespace}};
+
+                                           /// <summary>
+                                           ///     Used to enable source generation of a common set of extension methods for enum types.
+                                           /// </summary>
+                                           {{Strings.Templates.AttributesForGeneratedTypes}}
+                                           [System.AttributeUsageAttribute (System.AttributeTargets.Enum)]
+                                           public sealed class {{GeneratorAttributeName}} : Attribute
+                                           {
+                                               /// <summary>
+                                               ///     The name of the generated static class.
+                                               /// </summary>
+                                               /// <remarks>
+                                               ///     If unspecified, null, empty, or only whitespace, defaults to the name of the enum plus "Extensions".<br/>
+                                               ///     No other validation is performed, so illegal values will simply result in compiler errors.
+                                               ///     <para>
+                                               ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+                                               ///     </para>
+                                               /// </remarks>
+                                               public string? ClassName { get; set; }
+                                           
+                                               /// <summary>
+                                               ///     The namespace in which to place the generated static class containing the extension methods.
+                                               /// </summary>
+                                               /// <remarks>
+                                               ///     If unspecified, null, empty, or only whitespace, defaults to the namespace of the enum.<br/>
+                                               ///     No other validation is performed, so illegal values will simply result in compiler errors.
+                                               ///     <para>
+                                               ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+                                               ///     </para>
+                                               /// </remarks>
+                                               public string? ClassNamespace { get; set; }
+                                           
+                                               /// <summary>
+                                               ///     Whether to generate a fast, zero-allocation, non-boxing, and reflection-free alternative to the built-in
+                                               ///     <see cref="Enum.HasFlag"/> method.
+                                               /// </summary>
+                                               /// <remarks>
+                                               ///     <para>
+                                               ///         Default: false
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         If the enum is not decorated with <see cref="Flags"/>, this option has no effect.
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         If multiple members have the same value, the first member with that value will be used and subsequent members
+                                               ///         with the same value will be skipped.
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         Overloads taking the enum type itself as well as the underlying type of the enum will be generated, enabling
+                                               ///         avoidance of implicit or explicit cast overhead.
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+                                               ///     </para>
+                                               /// </remarks>
+                                               public bool FastHasFlags { get; set; }
+                                           
+                                               /// <summary>
+                                               ///     Whether to generate a fast, zero-allocation, and reflection-free alternative to the built-in
+                                               ///     <see cref="Enum.IsDefined"/> method,
+                                               ///     using a switch expression as a hard-coded reverse mapping of numeric values to explicitly-named members.
+                                               /// </summary>
+                                               /// <remarks>
+                                               ///     <para>
+                                               ///         Default: true
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         If multiple members have the same value, the first member with that value will be used and subsequent members
+                                               ///         with the same value will be skipped.
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         As with <see cref="Enum.IsDefined"/> the source generator only considers explicitly-named members.<br/>
+                                               ///         Generation of values which represent valid bitwise combinations of members of enums decorated with
+                                               ///         <see cref="Flags"/> is not affected by this property.
+                                               ///     </para>
+                                               /// </remarks>
+                                               public bool FastIsDefined { get; init; } = true;
+                                           
+                                               /// <summary>
+                                               ///     Gets a <see langword="bool"/> value indicating if this <see cref="GenerateEnumExtensionMethodsAttribute"/> instance
+                                               ///     contains default values only. See <see href="#remarks">remarks</see> of this method or documentation on properties of this type for details.
+                                               /// </summary>
+                                               /// <returns>
+                                               ///     A <see langword="bool"/> value indicating if all property values are default for this
+                                               ///     <see cref="GenerateEnumExtensionMethodsAttribute"/> instance.
+                                               /// </returns>
+                                               /// <remarks>
+                                               ///     Default values that will result in a <see langword="true"/> return value are:<br/>
+                                               ///     <see cref="FastIsDefined"/> &amp;&amp; !<see cref="FastHasFlags"/> &amp;&amp; <see cref="ClassName"/>
+                                               ///     <see langword="is"/> <see langword="null"/> &amp;&amp; <see cref="ClassNamespace"/> <see langword="is"/>
+                                               ///     <see langword="null"/>
+                                               /// </remarks>
+                                               public override bool IsDefaultAttribute ()
+                                               {
+                                                   return FastIsDefined
+                                                          && !FastHasFlags
+                                                          && ClassName is null
+                                                          && ClassNamespace is null;
+                                               }
+                                           }
+                                           
+                                           """,
+                                         Encoding.UTF8));
+
+        postInitializationContext
+            .AddSource (
+                        $"{ExtensionsForEnumTypeAttributeFullyQualifiedName}.g.cs",
+                        SourceText.From (
+                                         $$"""
+                                           // ReSharper disable RedundantNameQualifier
+                                           // ReSharper disable RedundantNullableDirective
+                                           // ReSharper disable UnusedType.Global
+                                           {{Strings.Templates.AutoGeneratedCommentBlock}}
+                                           #nullable enable
+
+                                           namespace {{Strings.AnalyzersAttributesNamespace}};
+
+                                           /// <summary>
+                                           ///     Attribute written by the source generator for enum extension classes, for easier analysis and reflection.
+                                           /// </summary>
+                                           /// <remarks>
+                                           ///     Properties are just convenient shortcuts to properties of <typeparamref name="TEnum"/>.
+                                           /// </remarks>
+                                           {{Strings.Templates.AttributesForGeneratedTypes}}
+                                           [System.AttributeUsageAttribute (System.AttributeTargets.Class | System.AttributeTargets.Interface)]
+                                           public sealed class {{ExtensionsForEnumTypeAttributeName}}<TEnum>: System.Attribute, IExtensionsForEnumTypeAttributes where TEnum : struct, Enum
+                                           {
+                                               /// <summary>
+                                               ///     The namespace-qualified name of <typeparamref name="TEnum"/>.
+                                               /// </summary>
+                                               public string EnumFullName => EnumType.FullName!;
+                                           
+                                               /// <summary>
+                                               ///     The unqualified name of <typeparamref name="TEnum"/>.
+                                               /// </summary>
+                                               public string EnumName => EnumType.Name;
+                                           
+                                               /// <summary>
+                                               ///     The namespace containing <typeparamref name="TEnum"/>.
+                                               /// </summary>
+                                               public string EnumNamespace => EnumType.Namespace!;
+                                           
+                                               /// <summary>
+                                               ///     The <see cref="Type"/> given by <see langword="typeof"/>(<typeparamref name="TEnum"/>).
+                                               /// </summary>
+                                               public Type EnumType => typeof (TEnum);
+                                           }
+                                           
+                                           """,
+                                         Encoding.UTF8));
+    }
+
+    [SuppressMessage ("Roslynator", "RCS1267", Justification = "Intentionally used so that Spans are used.")]
+    private static void GenerateDummyNamespaces (IncrementalGeneratorPostInitializationContext postInitializeContext)
+    {
+        postInitializeContext.AddSource (
+                                         string.Concat (Strings.InternalAnalyzersNamespace, "Namespaces.g.cs"),
+                                         SourceText.From (Strings.Templates.DummyNamespaceDeclarations, Encoding.UTF8));
+    }
+
+    private static void GenerateSourceFromGenerationInfo (SourceProductionContext context, EnumExtensionMethodsGenerationInfo? enumInfo)
+    {
+        // Just in case we still made it this far with a null...
+        if (enumInfo is not { })
+        {
+            return;
+        }
+
+        CodeWriter writer = new (enumInfo);
+
+        context.AddSource ($"{enumInfo.FullyQualifiedClassName}.g.cs", writer.GenerateSourceText ());
+    }
+
+    /// <summary>
+    ///     Returns true if <paramref name="syntaxNode"/> is an EnumDeclarationSyntax
+    ///     whose parent is a NamespaceDeclarationSyntax, FileScopedNamespaceDeclarationSyntax, or a
+    ///     (Class|Struct)DeclarationSyntax.<br/>
+    ///     Additional filtering is performed in later stages.
+    /// </summary>
+    private static bool IsPotentiallyInterestingDeclaration (SyntaxNode syntaxNode, CancellationToken cancellationToken)
+    {
+        cancellationToken.ThrowIfCancellationRequested ();
+
+        return syntaxNode is
+               {
+                   RawKind: 8858, //(int)SyntaxKind.EnumDeclaration,
+                   Parent.RawKind: 8845 //(int)SyntaxKind.FileScopedNamespaceDeclaration
+                                   or 8842 //(int)SyntaxKind.NamespaceDeclaration
+                                   or 8855 //(int)SyntaxKind.ClassDeclaration
+                                   or 8856 //(int)SyntaxKind.StructDeclaration
+                                   or 9068 //(int)SyntaxKind.RecordStructDeclaration
+                                   or 9063 //(int)SyntaxKind.RecordDeclaration
+               };
+    }
+}

+ 130 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumMemberCombinationsGenerator.cs

@@ -0,0 +1,130 @@
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Terminal.Gui.Analyzers.Internal.Attributes;
+using Terminal.Gui.Analyzers.Internal.Constants;
+
+namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
+
+/// <summary>
+/// Implementation of <see cref="IIncrementalGenerator"/> for types decorated with <see cref="GenerateEnumMemberCombinationsAttribute"/>.
+/// </summary>
+[Generator]
+internal sealed class EnumMemberCombinationsGenerator : IIncrementalGenerator
+{
+    private const string AttributeCodeText = $$"""
+                                               {{Strings.Templates.StandardHeader}}
+                                               
+                                               namespace {{Strings.AnalyzersAttributesNamespace}};
+                                               
+                                               /// <summary>
+                                               ///     Designates an enum member for inclusion in generation of bitwise combinations with other members decorated with
+                                               ///     this attribute which have the same <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.GroupTag)}}"/> value.<br/>
+                                               /// </summary>
+                                               /// <remarks>
+                                               ///     <para>
+                                               ///         This attribute is only considered for enum types with the <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute)}}"/>.
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         Masks with more than 8 bits set will
+                                               ///     </para>
+                                               /// </remarks>
+                                               [AttributeUsageAttribute(AttributeTargets.Enum)]
+                                               internal sealed class {{nameof (GenerateEnumMemberCombinationsAttribute)}} : System.Attribute
+                                               {
+                                                   public const byte MaximumPopCountLimit = 14;
+                                                   private uint _mask;
+                                                   private uint _maskPopCount;
+                                                   private byte _popCountLimit = 8;
+                                                   public required string GroupTag { get; set; }
+                                               
+                                                   public required uint Mask
+                                                   {
+                                                       get => _mask;
+                                                       set
+                                                       {
+                                                           _maskPopCount = uint.PopCount(value);
+                                               
+                                                           PopCountLimitExceeded = _maskPopCount > PopCountLimit;
+                                                           MaximumPopCountLimitExceeded = _maskPopCount > MaximumPopCountLimit;
+                                               
+                                                           if (PopCountLimitExceeded || MaximumPopCountLimitExceeded)
+                                                           {
+                                                               return;
+                                                           }
+                                               
+                                                           _mask = value;
+                                                       }
+                                                   }
+                                               
+                                                   /// <summary>
+                                                   ///     The maximum number of bits allowed to be set to 1 in <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>.
+                                                   /// </summary>
+                                                   /// <remarks>
+                                                   ///     <para>
+                                                   ///         Default: 8 (256 possible combinations)
+                                                   ///     </para>
+                                                   ///     <para>
+                                                   ///         Increasing this value is not recommended!<br/>
+                                                   ///         Decreasing this value is pointless unless you want to limit maximum possible generated combinations even
+                                                   ///         further.
+                                                   ///     </para>
+                                                   ///     <para>
+                                                   ///         If the result of <see cref="uint.PopCount"/>(<see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>) exceeds 2 ^ <see cref="PopCountLimit"/>, no
+                                                   ///         combinations will be generated for the members which otherwise would have been included by <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>.
+                                                   ///         Values exceeding the actual population count of <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/> have no effect.
+                                                   ///     </para>
+                                                   ///     <para>
+                                                   ///         This option is set to a sane default of 8, but also has a hard-coded limit of 14 (16384 combinations), as a
+                                                   ///         protection against generation of extremely large files.
+                                                   ///     </para>
+                                                   ///     <para>
+                                                   ///         CAUTION: The maximum number of possible combinations possible is equal to 1 &lt;&lt;
+                                                   ///         <see cref="uint.PopCount"/>(<see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>).
+                                                   ///         See <see cref="MaximumPopCountLimit"/> for hard-coded limit,
+                                                   ///     </para>
+                                                   /// </remarks>
+                                                   public byte PopCountLimit
+                                                   {
+                                                       get => _popCountLimit;
+                                                       set
+                                                       {
+                                                           _maskPopCount = uint.PopCount(_mask);
+                                               
+                                                           PopCountLimitExceeded = _maskPopCount > value;
+                                                           MaximumPopCountLimitExceeded = _maskPopCount > MaximumPopCountLimit;
+                                               
+                                                           if (PopCountLimitExceeded || MaximumPopCountLimitExceeded)
+                                                           {
+                                                               return;
+                                                           }
+                                               
+                                                           _mask = value;
+                                                           _popCountLimit = value;
+                                                       }
+                                                   }
+                                               
+                                                   internal bool MaximumPopCountLimitExceeded { get; private set; }
+                                                   internal bool PopCountLimitExceeded { get; private set; }
+                                               }
+
+                                               """;
+
+    private const string AttributeFullyQualifiedName = $"{Strings.AnalyzersAttributesNamespace}.{AttributeName}";
+    private const string AttributeName = "GenerateEnumMemberCombinationsAttribute";
+
+    /// <inheritdoc/>
+    public void Initialize (IncrementalGeneratorInitializationContext context)
+    {
+        context.RegisterPostInitializationOutput (GenerateAttributeCode);
+
+        return;
+
+        static void GenerateAttributeCode (IncrementalGeneratorPostInitializationContext initContext)
+        {
+#pragma warning disable IDE0061 // Use expression body for local function
+            initContext.AddSource ($"{AttributeFullyQualifiedName}.g.cs", SourceText.From (AttributeCodeText, Encoding.UTF8));
+#pragma warning restore IDE0061 // Use expression body for local function
+        }
+    }
+}

+ 38 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/IGeneratedTypeMetadata.cs

@@ -0,0 +1,38 @@
+using JetBrains.Annotations;
+using Microsoft.CodeAnalysis;
+
+namespace Terminal.Gui.Analyzers.Internal;
+
+/// <summary>
+/// Interface for all generators to use for their metadata classes.
+/// </summary>
+/// <typeparam name="TSelf">The type implementing this interface.</typeparam>
+internal interface IGeneratedTypeMetadata<out TSelf> where TSelf : IGeneratedTypeMetadata<TSelf>
+{
+    [UsedImplicitly]
+    string GeneratedTypeNamespace { get; }
+    [UsedImplicitly]
+    string? GeneratedTypeName { get; }
+    [UsedImplicitly]
+    string GeneratedTypeFullName { get; }
+    [UsedImplicitly]
+    string TargetTypeNamespace { get; }
+    [UsedImplicitly]
+    string TargetTypeName { get; }
+    string TargetTypeFullName { get; }
+    [UsedImplicitly]
+    Accessibility Accessibility { get; }
+    TypeKind TypeKind { get; }
+    bool IsRecord { get; }
+    bool IsClass { get; }
+    bool IsStruct { get; }
+    [UsedImplicitly]
+    bool IsPartial { get; }
+    bool IsByRefLike { get; }
+    bool IsSealed { get; }
+    bool IsAbstract { get; }
+    bool IsEnum { get; }
+    bool IsStatic { get; }
+    [UsedImplicitly]
+    bool IncludeInterface { get; }
+}

+ 28 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/IStandardCSharpCodeGenerator.cs

@@ -0,0 +1,28 @@
+using System.Text;
+using JetBrains.Annotations;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Terminal.Gui.Analyzers.Internal;
+
+internal interface IStandardCSharpCodeGenerator<T> where T : IGeneratedTypeMetadata<T>
+{
+    /// <summary>
+    ///     Generates and returns the full source text corresponding to <see cref="Metadata"/>,
+    ///     in the requested <paramref name="encoding"/> or <see cref="Encoding.UTF8"/> if not provided.
+    /// </summary>
+    /// <param name="encoding">
+    ///     The <see cref="Encoding"/> of the generated source text or <see cref="Encoding.UTF8"/> if not
+    ///     provided.
+    /// </param>
+    /// <returns></returns>
+    [UsedImplicitly]
+    [SkipLocalsInit]
+    ref readonly SourceText GenerateSourceText (Encoding? encoding = null);
+
+    /// <summary>
+    ///     A type implementing <see cref="IGeneratedTypeMetadata{T}"/> which
+    ///     will be used for source generation.
+    /// </summary>
+    [UsedImplicitly]
+    T Metadata { get; set; }
+}