EnumMemberCombinationsGenerator.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. using System.Text;
  2. using Microsoft.CodeAnalysis;
  3. using Microsoft.CodeAnalysis.Text;
  4. using Terminal.Gui.Analyzers.Internal.Attributes;
  5. using Terminal.Gui.Analyzers.Internal.Constants;
  6. namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
  7. /// <summary>
  8. /// Implementation of <see cref="IIncrementalGenerator"/> for types decorated with <see cref="GenerateEnumMemberCombinationsAttribute"/>.
  9. /// </summary>
  10. [Generator]
  11. internal sealed class EnumMemberCombinationsGenerator : IIncrementalGenerator
  12. {
  13. private const string AttributeCodeText = $$"""
  14. {{Strings.Templates.StandardHeader}}
  15. namespace {{Strings.AnalyzersAttributesNamespace}};
  16. /// <summary>
  17. /// Designates an enum member for inclusion in generation of bitwise combinations with other members decorated with
  18. /// this attribute which have the same <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.GroupTag)}}"/> value.<br/>
  19. /// </summary>
  20. /// <remarks>
  21. /// <para>
  22. /// This attribute is only considered for enum types with the <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute)}}"/>.
  23. /// </para>
  24. /// <para>
  25. /// Masks with more than 8 bits set will
  26. /// </para>
  27. /// </remarks>
  28. [AttributeUsageAttribute(AttributeTargets.Enum)]
  29. internal sealed class {{nameof (GenerateEnumMemberCombinationsAttribute)}} : System.Attribute
  30. {
  31. public const byte MaximumPopCountLimit = 14;
  32. private uint _mask;
  33. private uint _maskPopCount;
  34. private byte _popCountLimit = 8;
  35. public required string GroupTag { get; set; }
  36. public required uint Mask
  37. {
  38. get => _mask;
  39. set
  40. {
  41. _maskPopCount = uint.PopCount(value);
  42. PopCountLimitExceeded = _maskPopCount > PopCountLimit;
  43. MaximumPopCountLimitExceeded = _maskPopCount > MaximumPopCountLimit;
  44. if (PopCountLimitExceeded || MaximumPopCountLimitExceeded)
  45. {
  46. return;
  47. }
  48. _mask = value;
  49. }
  50. }
  51. /// <summary>
  52. /// The maximum number of bits allowed to be set to 1 in <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>.
  53. /// </summary>
  54. /// <remarks>
  55. /// <para>
  56. /// Default: 8 (256 possible combinations)
  57. /// </para>
  58. /// <para>
  59. /// Increasing this value is not recommended!<br/>
  60. /// Decreasing this value is pointless unless you want to limit maximum possible generated combinations even
  61. /// further.
  62. /// </para>
  63. /// <para>
  64. /// If the result of <see cref="uint.PopCount"/>(<see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>) exceeds 2 ^ <see cref="PopCountLimit"/>, no
  65. /// combinations will be generated for the members which otherwise would have been included by <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>.
  66. /// Values exceeding the actual population count of <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/> have no effect.
  67. /// </para>
  68. /// <para>
  69. /// This option is set to a sane default of 8, but also has a hard-coded limit of 14 (16384 combinations), as a
  70. /// protection against generation of extremely large files.
  71. /// </para>
  72. /// <para>
  73. /// CAUTION: The maximum number of possible combinations possible is equal to 1 &lt;&lt;
  74. /// <see cref="uint.PopCount"/>(<see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>).
  75. /// See <see cref="MaximumPopCountLimit"/> for hard-coded limit,
  76. /// </para>
  77. /// </remarks>
  78. public byte PopCountLimit
  79. {
  80. get => _popCountLimit;
  81. set
  82. {
  83. _maskPopCount = uint.PopCount(_mask);
  84. PopCountLimitExceeded = _maskPopCount > value;
  85. MaximumPopCountLimitExceeded = _maskPopCount > MaximumPopCountLimit;
  86. if (PopCountLimitExceeded || MaximumPopCountLimitExceeded)
  87. {
  88. return;
  89. }
  90. _mask = value;
  91. _popCountLimit = value;
  92. }
  93. }
  94. internal bool MaximumPopCountLimitExceeded { get; private set; }
  95. internal bool PopCountLimitExceeded { get; private set; }
  96. }
  97. """;
  98. private const string AttributeFullyQualifiedName = $"{Strings.AnalyzersAttributesNamespace}.{AttributeName}";
  99. private const string AttributeName = "GenerateEnumMemberCombinationsAttribute";
  100. /// <inheritdoc/>
  101. public void Initialize (IncrementalGeneratorInitializationContext context)
  102. {
  103. context.RegisterPostInitializationOutput (GenerateAttributeCode);
  104. return;
  105. static void GenerateAttributeCode (IncrementalGeneratorPostInitializationContext initContext)
  106. {
  107. #pragma warning disable IDE0061 // Use expression body for local function
  108. initContext.AddSource ($"{AttributeFullyQualifiedName}.g.cs", SourceText.From (AttributeCodeText, Encoding.UTF8));
  109. #pragma warning restore IDE0061 // Use expression body for local function
  110. }
  111. }
  112. }