From b6ce1618cec09ce1615856c515247a75e0f7edfb Mon Sep 17 00:00:00 2001 From: Nick Cipollina Date: Tue, 31 Mar 2026 10:37:08 -0400 Subject: [PATCH 1/3] feat: support abstract intermediate base classes in generator Skip abstract classes in EnumSyntaxProvider so that CRTP-style abstract base classes (e.g. DomainEnum : OptimizedEnum) no longer trigger spurious OE0004 diagnostics. Concrete subclasses are still processed normally with TEnum correctly resolved to the concrete type via the full inheritance chain walk. Add two tests: - AbstractBase_WithCRTP: verifies exactly one tree is generated for the concrete subclass with no diagnostics from the abstract base - AbstractBase_Alone_ProducesNoOutput: verifies a standalone abstract base produces zero output and zero diagnostics Bump version to 1.1.1. Co-Authored-By: Claude Sonnet 4.6 --- Directory.Build.props | 2 +- .../Providers/EnumSyntaxProvider.cs | 4 + .../GeneratorVerifyTests.cs | 68 +++++++++++++ ...RTP#MyApp.Domain.OrderStatus.g.verified.cs | 98 +++++++++++++++++++ 4 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.AbstractBase_WithCRTP#MyApp.Domain.OrderStatus.g.verified.cs diff --git a/Directory.Build.props b/Directory.Build.props index 72560e1..cf82bab 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.1.0 + 1.1.1 MIT https://github.com/layeredcraft/optimized-enums git diff --git a/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs b/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs index d03c275..20b4660 100644 --- a/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs +++ b/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs @@ -32,6 +32,10 @@ internal static bool Predicate(SyntaxNode node, CancellationToken _) => if (baseType is null) return null; + // Skip abstract classes — they are intermediate base classes, not concrete enums + if (classSymbol.IsAbstract) + return null; + var diagnostics = new List(); var location = classDecl.CreateLocationInfo(); var className = classSymbol.Name; diff --git a/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs index 04e0744..975318b 100644 --- a/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs +++ b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs @@ -261,6 +261,74 @@ private OrderStatus(int value, string name) : base(value, name) { } }, TestContext.Current.CancellationToken); + [Fact] + public async Task AbstractBase_WithCRTP() => + await GeneratorTestHelpers.Verify( + new VerifyTestOptions + { + SourceCode = """ + using LayeredCraft.OptimizedEnums; + + namespace MyApp.Domain; + + public abstract partial class DomainEnum : OptimizedEnum + where TSelf : DomainEnum + { + public string Description { get; } + + protected DomainEnum(int value, string name, string description) + : base(value, name) + { + Description = description; + } + } + + public sealed partial class OrderStatus : DomainEnum + { + public static readonly OrderStatus Pending = new(1, nameof(Pending), "Order is pending"); + public static readonly OrderStatus Paid = new(2, nameof(Paid), "Payment received"); + + private OrderStatus(int value, string name, string description) + : base(value, name, description) { } + } + """, + ExpectedTrees = 1, + }, + TestContext.Current.CancellationToken); + + [Fact] + public async Task AbstractBase_Alone_ProducesNoOutput() + { + var options = new VerifyTestOptions + { + SourceCode = """ + using LayeredCraft.OptimizedEnums; + + namespace MyApp.Domain; + + public abstract partial class DomainEnum : OptimizedEnum + where TSelf : DomainEnum + { + public string Description { get; } + + protected DomainEnum(int value, string name, string description) + : base(value, name) + { + Description = description; + } + } + """, + ExpectedTrees = 0, + }; + + var (driver, _) = GeneratorTestHelpers.GenerateFromSource(options, TestContext.Current.CancellationToken); + + var result = driver.GetRunResult(); + + result.Diagnostics.Should().BeEmpty("abstract base class alone should produce no diagnostics"); + result.GeneratedTrees.Length.Should().Be(0, "abstract base class alone should produce no generated output"); + } + [Fact] public async Task Error_OE0005_DuplicateValue_IsEmitted() => await GeneratorTestHelpers.VerifyFailure( diff --git a/tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.AbstractBase_WithCRTP#MyApp.Domain.OrderStatus.g.verified.cs b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.AbstractBase_WithCRTP#MyApp.Domain.OrderStatus.g.verified.cs new file mode 100644 index 0000000..8c3d17f --- /dev/null +++ b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.AbstractBase_WithCRTP#MyApp.Domain.OrderStatus.g.verified.cs @@ -0,0 +1,98 @@ +//HintName: MyApp.Domain.OrderStatus.g.cs +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable + +namespace MyApp.Domain; + +[global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] +partial class OrderStatus +{ + private static readonly global::System.Collections.ObjectModel.ReadOnlyCollection s_all = + global::System.Array.AsReadOnly(new global::MyApp.Domain.OrderStatus[] + { + Pending, + Paid + }); + + private static readonly global::System.Collections.ObjectModel.ReadOnlyCollection s_names = + global::System.Array.AsReadOnly(new string[] + { + Pending.Name, + Paid.Name + }); + + private static readonly global::System.Collections.ObjectModel.ReadOnlyCollection s_values = + global::System.Array.AsReadOnly(new int[] + { + Pending.Value, + Paid.Value + }); + + private static readonly global::System.Collections.Generic.Dictionary s_byName = + new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.Ordinal) + { + [Pending.Name] = Pending, + [Paid.Name] = Paid + }; + + private static readonly global::System.Collections.Generic.Dictionary s_byValue = + new global::System.Collections.Generic.Dictionary(2) + { + [Pending.Value] = Pending, + [Paid.Value] = Paid + }; + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static global::System.Collections.Generic.IReadOnlyList All => s_all; + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static global::System.Collections.Generic.IReadOnlyList Names => s_names; + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static global::System.Collections.Generic.IReadOnlyList Values => s_values; + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public const int Count = 2; + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static global::MyApp.Domain.OrderStatus FromName(string name) + { + if (!s_byName.TryGetValue(name, out var result)) + throw new global::System.Collections.Generic.KeyNotFoundException( + $"'{name}' is not a valid name for OrderStatus"); + + return result; + } + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static bool TryFromName(string name, out global::MyApp.Domain.OrderStatus? result) => + s_byName.TryGetValue(name, out result); + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static global::MyApp.Domain.OrderStatus FromValue(int value) + { + if (!s_byValue.TryGetValue(value, out var result)) + throw new global::System.Collections.Generic.KeyNotFoundException( + $"'{value}' is not a valid value for OrderStatus"); + + return result; + } + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static bool TryFromValue(int value, out global::MyApp.Domain.OrderStatus? result) => + s_byValue.TryGetValue(value, out result); + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static bool ContainsName(string name) => s_byName.ContainsKey(name); + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static bool ContainsValue(int value) => s_byValue.ContainsKey(value); +} From eef4cb0e151246ad630bf127b397d7710cad1fe1 Mon Sep 17 00:00:00 2001 From: Nick Cipollina Date: Tue, 31 Mar 2026 11:01:40 -0400 Subject: [PATCH 2/3] fix: only skip abstract classes with no eligible members Abstract classes that declare members (e.g. abstract enums with concrete nested implementations) should still generate normally. The IsAbstract guard now lives inside the `validMembers.Count == 0` block, so only memberless abstract base classes are skipped silently. Adds AbstractEnum_WithMembers_StillGenerates regression test and updates assertion messages for AbstractBase_Alone_ProducesNoOutput. Co-Authored-By: Claude Sonnet 4.6 --- .../Providers/EnumSyntaxProvider.cs | 10 +- .../GeneratorVerifyTests.cs | 30 +++++- ...enerates#MyApp.Domain.Status.g.verified.cs | 98 +++++++++++++++++++ 3 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.AbstractEnum_WithMembers_StillGenerates#MyApp.Domain.Status.g.verified.cs diff --git a/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs b/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs index 20b4660..2848d2f 100644 --- a/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs +++ b/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs @@ -32,10 +32,6 @@ internal static bool Predicate(SyntaxNode node, CancellationToken _) => if (baseType is null) return null; - // Skip abstract classes — they are intermediate base classes, not concrete enums - if (classSymbol.IsAbstract) - return null; - var diagnostics = new List(); var location = classDecl.CreateLocationInfo(); var className = classSymbol.Name; @@ -125,8 +121,14 @@ internal static bool Predicate(SyntaxNode node, CancellationToken _) => DetectDuplicateValues(classSymbol, context.SemanticModel, validMembers, diagnostics, className, cancellationToken); // OE0004: no valid members + // Abstract classes with no eligible members are intermediate base classes — skip silently. + // Abstract classes that do declare members (e.g. via nested concrete implementations) are + // still valid enums and should generate normally. if (validMembers.Count == 0) { + if (classSymbol.IsAbstract) + return null; + diagnostics.Add(new DiagnosticInfo( DiagnosticDescriptors.NoMembersFound, location, diff --git a/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs index 975318b..78093b7 100644 --- a/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs +++ b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs @@ -325,10 +325,36 @@ protected DomainEnum(int value, string name, string description) var result = driver.GetRunResult(); - result.Diagnostics.Should().BeEmpty("abstract base class alone should produce no diagnostics"); - result.GeneratedTrees.Length.Should().Be(0, "abstract base class alone should produce no generated output"); + result.Diagnostics.Should().BeEmpty("abstract intermediate base class should produce no diagnostics"); + result.GeneratedTrees.Length.Should().Be(0, "abstract intermediate base class should produce no generated output"); } + [Fact] + public async Task AbstractEnum_WithMembers_StillGenerates() => + await GeneratorTestHelpers.Verify( + new VerifyTestOptions + { + SourceCode = """ + using LayeredCraft.OptimizedEnums; + + namespace MyApp.Domain; + + public abstract partial class Status : OptimizedEnum + { + public static readonly Status Active = null!; + public static readonly Status Inactive = null!; + + protected Status(int value, string name) : base(value, name) { } + } + """, + DiagnosticsToSuppress = new Dictionary + { + ["OE0101"] = ReportDiagnostic.Suppress, + }, + ExpectedTrees = 1, + }, + TestContext.Current.CancellationToken); + [Fact] public async Task Error_OE0005_DuplicateValue_IsEmitted() => await GeneratorTestHelpers.VerifyFailure( diff --git a/tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.AbstractEnum_WithMembers_StillGenerates#MyApp.Domain.Status.g.verified.cs b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.AbstractEnum_WithMembers_StillGenerates#MyApp.Domain.Status.g.verified.cs new file mode 100644 index 0000000..82baaeb --- /dev/null +++ b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.AbstractEnum_WithMembers_StillGenerates#MyApp.Domain.Status.g.verified.cs @@ -0,0 +1,98 @@ +//HintName: MyApp.Domain.Status.g.cs +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable + +namespace MyApp.Domain; + +[global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] +partial class Status +{ + private static readonly global::System.Collections.ObjectModel.ReadOnlyCollection s_all = + global::System.Array.AsReadOnly(new global::MyApp.Domain.Status[] + { + Active, + Inactive + }); + + private static readonly global::System.Collections.ObjectModel.ReadOnlyCollection s_names = + global::System.Array.AsReadOnly(new string[] + { + Active.Name, + Inactive.Name + }); + + private static readonly global::System.Collections.ObjectModel.ReadOnlyCollection s_values = + global::System.Array.AsReadOnly(new int[] + { + Active.Value, + Inactive.Value + }); + + private static readonly global::System.Collections.Generic.Dictionary s_byName = + new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.Ordinal) + { + [Active.Name] = Active, + [Inactive.Name] = Inactive + }; + + private static readonly global::System.Collections.Generic.Dictionary s_byValue = + new global::System.Collections.Generic.Dictionary(2) + { + [Active.Value] = Active, + [Inactive.Value] = Inactive + }; + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static global::System.Collections.Generic.IReadOnlyList All => s_all; + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static global::System.Collections.Generic.IReadOnlyList Names => s_names; + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static global::System.Collections.Generic.IReadOnlyList Values => s_values; + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public const int Count = 2; + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static global::MyApp.Domain.Status FromName(string name) + { + if (!s_byName.TryGetValue(name, out var result)) + throw new global::System.Collections.Generic.KeyNotFoundException( + $"'{name}' is not a valid name for Status"); + + return result; + } + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static bool TryFromName(string name, out global::MyApp.Domain.Status? result) => + s_byName.TryGetValue(name, out result); + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static global::MyApp.Domain.Status FromValue(int value) + { + if (!s_byValue.TryGetValue(value, out var result)) + throw new global::System.Collections.Generic.KeyNotFoundException( + $"'{value}' is not a valid value for Status"); + + return result; + } + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static bool TryFromValue(int value, out global::MyApp.Domain.Status? result) => + s_byValue.TryGetValue(value, out result); + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static bool ContainsName(string name) => s_byName.ContainsKey(name); + + [global::System.CodeDom.Compiler.GeneratedCode("LayeredCraft.OptimizedEnums.Generator", "REPLACED")] + public static bool ContainsValue(int value) => s_byValue.ContainsKey(value); +} From 75986bba7d2d437e3ec68f9ca756f115d917ef89 Mon Sep 17 00:00:00 2001 From: Nick Cipollina Date: Tue, 31 Mar 2026 11:19:55 -0400 Subject: [PATCH 3/3] fix: surface diagnostics for abstract types with only invalid members - OE0101 (non-private constructor) is now suppressed for abstract classes since a protected constructor is idiomatic and required so concrete subclasses can invoke base(...) - Abstract classes with no valid members but collected diagnostics (e.g. OE0102 on non-readonly fields) now return an EnumInfo so those diagnostics are reported; only truly clean abstract bases (no diagnostics) are silently skipped - Removes the now-unnecessary DiagnosticsToSuppress for OE0101 in AbstractEnum_WithMembers_StillGenerates test - Adds Warning_AbstractBase_WithOnlyInvalidMembers_StillSurfacesDiagnostics regression test verifying OE0102 is emitted for abstract types Co-Authored-By: Claude Sonnet 4.6 --- .../Providers/EnumSyntaxProvider.cs | 41 +++++++++++-------- .../GeneratorVerifyTests.cs | 26 ++++++++++-- ...bers_StillSurfacesDiagnostics.verified.txt | 18 ++++++++ 3 files changed, 64 insertions(+), 21 deletions(-) create mode 100644 tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.Warning_AbstractBase_WithOnlyInvalidMembers_StillSurfacesDiagnostics.verified.txt diff --git a/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs b/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs index 2848d2f..4a73813 100644 --- a/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs +++ b/src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs @@ -62,17 +62,22 @@ internal static bool Predicate(SyntaxNode node, CancellationToken _) => cancellationToken.ThrowIfCancellationRequested(); - // OE0101: Warn about non-private constructors - foreach (var ctor in classSymbol.Constructors) + // OE0101: Warn about non-private constructors. + // Skip for abstract classes: a protected constructor is idiomatic and required + // so that concrete subclasses can invoke base(...). + if (!classSymbol.IsAbstract) { - if (ctor.IsImplicitlyDeclared) - continue; - if (ctor.DeclaredAccessibility != Accessibility.Private) + foreach (var ctor in classSymbol.Constructors) { - diagnostics.Add(new DiagnosticInfo( - DiagnosticDescriptors.NonPrivateConstructor, - ctor.CreateLocationInfo(), - className)); + if (ctor.IsImplicitlyDeclared) + continue; + if (ctor.DeclaredAccessibility != Accessibility.Private) + { + diagnostics.Add(new DiagnosticInfo( + DiagnosticDescriptors.NonPrivateConstructor, + ctor.CreateLocationInfo(), + className)); + } } } @@ -121,18 +126,20 @@ internal static bool Predicate(SyntaxNode node, CancellationToken _) => DetectDuplicateValues(classSymbol, context.SemanticModel, validMembers, diagnostics, className, cancellationToken); // OE0004: no valid members - // Abstract classes with no eligible members are intermediate base classes — skip silently. - // Abstract classes that do declare members (e.g. via nested concrete implementations) are - // still valid enums and should generate normally. + // Abstract classes with no eligible members and no diagnostics are intermediate base + // classes — skip silently so they produce no output and no noise. + // If diagnostics were collected (e.g. OE0101, OE0102) we still surface them by falling + // through to the EnumInfo return; the generator skips code emission for empty member lists. if (validMembers.Count == 0) { - if (classSymbol.IsAbstract) + if (classSymbol.IsAbstract && diagnostics.Count == 0) return null; - diagnostics.Add(new DiagnosticInfo( - DiagnosticDescriptors.NoMembersFound, - location, - className)); + if (!classSymbol.IsAbstract) + diagnostics.Add(new DiagnosticInfo( + DiagnosticDescriptors.NoMembersFound, + location, + className)); } return new EnumInfo( diff --git a/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs index 78093b7..7aeb762 100644 --- a/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs +++ b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs @@ -347,14 +347,32 @@ public abstract partial class Status : OptimizedEnum protected Status(int value, string name) : base(value, name) { } } """, - DiagnosticsToSuppress = new Dictionary - { - ["OE0101"] = ReportDiagnostic.Suppress, - }, ExpectedTrees = 1, }, TestContext.Current.CancellationToken); + [Fact] + public async Task Warning_AbstractBase_WithOnlyInvalidMembers_StillSurfacesDiagnostics() => + await GeneratorTestHelpers.VerifyFailure( + new VerifyTestOptions + { + SourceCode = """ + using LayeredCraft.OptimizedEnums; + + namespace MyApp.Domain; + + public abstract partial class DomainEnum : OptimizedEnum + where TSelf : DomainEnum + { + public static DomainEnum BadField = null!; + + protected DomainEnum(int value, string name) : base(value, name) { } + } + """, + ExpectedDiagnosticId = "OE0102", + }, + TestContext.Current.CancellationToken); + [Fact] public async Task Error_OE0005_DuplicateValue_IsEmitted() => await GeneratorTestHelpers.VerifyFailure( diff --git a/tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.Warning_AbstractBase_WithOnlyInvalidMembers_StillSurfacesDiagnostics.verified.txt b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.Warning_AbstractBase_WithOnlyInvalidMembers_StillSurfacesDiagnostics.verified.txt new file mode 100644 index 0000000..1d13667 --- /dev/null +++ b/tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.Warning_AbstractBase_WithOnlyInvalidMembers_StillSurfacesDiagnostics.verified.txt @@ -0,0 +1,18 @@ +{ + Diagnostics: [ + { + Location: Program.cs: (7,36)-(7,44), + Message: The field 'BadField' in class 'DomainEnum' is a public static field of the enum type but is not readonly, + Severity: Warning, + WarningLevel: 1, + Descriptor: { + Id: OE0102, + Title: OptimizedEnum static field should be readonly, + MessageFormat: The field '{0}' in class '{1}' is a public static field of the enum type but is not readonly, + Category: OptimizedEnums.Usage, + DefaultSeverity: Warning, + IsEnabledByDefault: true + } + } + ] +} \ No newline at end of file