From 848870e3b009dec963be4e811bd6bc213a4a3f32 Mon Sep 17 00:00:00 2001 From: Jake Carpenter Date: Sun, 29 Mar 2026 11:17:11 -0600 Subject: [PATCH 1/2] fix: recognize types in test to debug source gen --- Optify.Tests/DebugGenerator.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Optify.Tests/DebugGenerator.cs b/Optify.Tests/DebugGenerator.cs index 214e9fc..499dbc3 100644 --- a/Optify.Tests/DebugGenerator.cs +++ b/Optify.Tests/DebugGenerator.cs @@ -12,7 +12,10 @@ public Task Should_allow_the_debugger_in_the_generator() var compilation = CSharpCompilation .Create("CSharpCodeGen.GenerateAssembly") .AddSyntaxTrees(CSharpSyntaxTree.ParseText(DebugGeneratorSource.Source)) - .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)) + .AddReferences( + MetadataReference.CreateFromFile(typeof(object).Assembly.Location), + MetadataReference.CreateFromFile(typeof(OptifyOptionsAttribute).Assembly.Location) + ) .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); var driver = CSharpGeneratorDriver .Create(generator) @@ -30,7 +33,8 @@ file static class DebugGeneratorSource using Optify; namespace CSharpCodeGen; - [OptifyOptions] public class Config; + [OptifyOptions] + public class Config; [OptifyOptions(SectionName = "OverrideNamedDummySettings")] public class NamedDummySettings; From 63ee16084abc782f2fe675763175bff93378f75b Mon Sep 17 00:00:00 2001 From: Jake Carpenter Date: Sun, 29 Mar 2026 11:45:30 -0600 Subject: [PATCH 2/2] refactor: clean up generator --- Optify/OptifyGenerator.cs | 21 +++++++-------------- Optify/OptionsTypeToRegister.cs | 33 ++++++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/Optify/OptifyGenerator.cs b/Optify/OptifyGenerator.cs index 767cfd9..4a109c7 100644 --- a/Optify/OptifyGenerator.cs +++ b/Optify/OptifyGenerator.cs @@ -17,24 +17,17 @@ public void Initialize(IncrementalGeneratorInitializationContext context) if (ctx.TargetSymbol is not INamedTypeSymbol target) return new OptionsTypeToRegister(); - var fullName = target.ToDisplayString( - SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle( - SymbolDisplayGlobalNamespaceStyle.Omitted - ) - ); - var sectionNameArg = ctx.Attributes[0] - .NamedArguments.FirstOrDefault(static x => x.Key == nameof(OptifyOptionsAttribute.SectionName)); - var sectionName = sectionNameArg.Value.Value as string ?? ctx.TargetSymbol.Name; - var validationArg = ctx.Attributes[0] - .NamedArguments.FirstOrDefault(static x => x.Key == nameof(OptifyOptionsAttribute.Validation)); - var validation = validationArg.Value.Value is int v ? v : 0; - - return new OptionsTypeToRegister(sectionName, fullName, (ValidationFlag)validation); + // At this point, ctx.Attributes *only* contains OptifyOptionsAttribute, even + // when other attributes are on the type. A user should receive a compiler error + // CS0579 if they added more than one [OptifyOptions]. However, generated source + // may be able to bypass that. In such a case, we're just going to assume + // that we'll take the first one. + return new OptionsTypeToRegister(ctx.Attributes[0], target); } ); context.RegisterSourceOutput( - provider.Where(static opt => !opt.IsNull).Collect(), + provider.Where(static opt => opt.IsValid).Collect(), static (spc, types) => { spc.AddSource(OptifyRegistrationSource.Filename, OptifyRegistrationSource.GenerateSource(types)); diff --git a/Optify/OptionsTypeToRegister.cs b/Optify/OptionsTypeToRegister.cs index d001e70..c8297a4 100644 --- a/Optify/OptionsTypeToRegister.cs +++ b/Optify/OptionsTypeToRegister.cs @@ -1,21 +1,36 @@ +using Microsoft.CodeAnalysis; + namespace Optify; public readonly record struct OptionsTypeToRegister { - public readonly bool IsNull; - public readonly string SectionName = string.Empty; - public readonly string FullName = string.Empty; - public readonly ValidationFlag Validation = 0; + public bool IsValid { get; } + public string SectionName { get; } = string.Empty; + public string FullName { get; } = string.Empty; + public ValidationFlag Validation { get; } - public OptionsTypeToRegister(string sectionName, string fullName, ValidationFlag validation) + public OptionsTypeToRegister(AttributeData attributeData, INamedTypeSymbol targetType) { - SectionName = sectionName; - FullName = fullName; - Validation = validation; + FullName = targetType.ToDisplayString(); + SectionName = targetType.Name; + IsValid = true; + + foreach (var argument in attributeData.NamedArguments) + { + switch (argument.Key) + { + case nameof(OptifyOptionsAttribute.SectionName): + SectionName = argument.Value.Value as string ?? targetType.Name; + break; + case nameof(OptifyOptionsAttribute.Validation): + Validation = argument.Value.Value is int intValue ? (ValidationFlag)intValue : 0; + break; + } + } } public OptionsTypeToRegister() { - IsNull = true; + IsValid = false; } }