From dbafdb2fb6f2c03696c182e876256578e040081a Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Fri, 26 Sep 2025 19:03:09 +0100 Subject: [PATCH 01/15] CompilationProvider is unused --- BitsKit.Generator/BitObjectGenerator.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/BitsKit.Generator/BitObjectGenerator.cs b/BitsKit.Generator/BitObjectGenerator.cs index c6e2f71..fed2dd4 100644 --- a/BitsKit.Generator/BitObjectGenerator.cs +++ b/BitsKit.Generator/BitObjectGenerator.cs @@ -22,10 +22,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .WithComparer(TypeSymbolProcessorComparer.Default) .Where(x => x is not null)!; - IncrementalValueProvider<(Compilation, ImmutableArray)> model = context - .CompilationProvider - .Combine(typeDeclarations.Collect()); - + var model = typeDeclarations.Collect(); context.RegisterSourceOutput(model, GenerateSourceCode); } @@ -46,15 +43,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return new(typeSymbol, typeDeclaration, attribute); } - private static void GenerateSourceCode(SourceProductionContext context, (Compilation _, ImmutableArray Processors) result) + private static void GenerateSourceCode(SourceProductionContext context, ImmutableArray processors) { - if (result.Processors.Length == 0) + if (processors.Length == 0) return; StringBuilder stringBuilder = new(StringConstants.Header); // group the objects by their respective namespace - var namespaceGroups = result.Processors.GroupBy(x => x.Namespace); + var namespaceGroups = processors.GroupBy(x => x.Namespace); foreach (var namespaceGroup in namespaceGroups) { From 178deddf47adb396cb57561c3b3a017294c60e9f Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 00:32:22 +0100 Subject: [PATCH 02/15] move MustBePartial and NestedNotAllowed diagnostics to an analyser --- BitsKit.Generator/BitObjectAnalyser.cs | 59 ++++++++++++++++++++++++ BitsKit.Generator/DiagnosticValidator.cs | 20 -------- BitsKit.Generator/SymbolExtensions.cs | 24 ++++++++++ BitsKit.Generator/TypeSymbolProcessor.cs | 4 -- BitsKit.Tests/GeneratorTests.cs | 8 ++-- 5 files changed, 87 insertions(+), 28 deletions(-) create mode 100644 BitsKit.Generator/BitObjectAnalyser.cs create mode 100644 BitsKit.Generator/SymbolExtensions.cs diff --git a/BitsKit.Generator/BitObjectAnalyser.cs b/BitsKit.Generator/BitObjectAnalyser.cs new file mode 100644 index 0000000..37e2760 --- /dev/null +++ b/BitsKit.Generator/BitObjectAnalyser.cs @@ -0,0 +1,59 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace BitsKit.Generator +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class BitObjectAnalyser : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics { get; } = [ + DiagnosticDescriptors.MustBePartial, + DiagnosticDescriptors.NestedNotAllowed + ]; + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(context => + { + var bitObjectAttribute = context.Compilation.GetTypeByMetadataName("BitsKit.BitFields.BitObjectAttribute"); + if (bitObjectAttribute == null) return; + + context.RegisterSymbolAction(context => + { + var type = (INamedTypeSymbol)context.Symbol; + + if (!type.TryGetAttributeWithType(bitObjectAttribute, out _)) + { + return; + } + + if (type.DeclaringSyntaxReferences[0].GetSyntax() is not TypeDeclarationSyntax typeDeclarationSyntax) + { + return; + } + + if (!typeDeclarationSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword))) + { + context.ReportDiagnostic( + Diagnostic.Create(DiagnosticDescriptors.MustBePartial, typeDeclarationSyntax.GetLocation(), type.Name) + ); + } + + if (type.ContainingType != null) + { + context.ReportDiagnostic( + Diagnostic.Create(DiagnosticDescriptors.NestedNotAllowed, typeDeclarationSyntax.GetLocation(), type.Name) + ); + } + }, SymbolKind.NamedType); + }); + } + } +} \ No newline at end of file diff --git a/BitsKit.Generator/DiagnosticValidator.cs b/BitsKit.Generator/DiagnosticValidator.cs index ac1d9ed..cd226c0 100644 --- a/BitsKit.Generator/DiagnosticValidator.cs +++ b/BitsKit.Generator/DiagnosticValidator.cs @@ -19,26 +19,6 @@ public static bool ReportDiagnostic(SourceProductionContext context, DiagnosticD return descriptor is { DefaultSeverity: DiagnosticSeverity.Error }; } - public static bool IsNotPartial(SourceProductionContext context, TypeDeclarationSyntax typeDeclaration, string typeName) - { - return !typeDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)) - && ReportDiagnostic( - context, - DiagnosticDescriptors.MustBePartial, - typeDeclaration.GetLocation(), - typeName); - } - - public static bool IsNested(SourceProductionContext context, TypeDeclarationSyntax typeDeclaration, string typeName) - { - return typeDeclaration.Parent is TypeDeclarationSyntax - && ReportDiagnostic( - context, - DiagnosticDescriptors.NestedNotAllowed, - typeDeclaration.GetLocation(), - typeName); - } - public static bool HasMissingFieldType(SourceProductionContext context, BitFieldModel bitField, string typeName) { return bitField.FieldType is null diff --git a/BitsKit.Generator/SymbolExtensions.cs b/BitsKit.Generator/SymbolExtensions.cs new file mode 100644 index 0000000..aa5aaed --- /dev/null +++ b/BitsKit.Generator/SymbolExtensions.cs @@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; + +namespace BitsKit.Generator +{ + public static class SymbolExtensions + { + public static bool TryGetAttributeWithType(this ISymbol symbol, ITypeSymbol typeSymbol, [NotNullWhen(true)] out AttributeData? attributeData) + { + foreach (AttributeData attribute in symbol.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, typeSymbol)) + { + attributeData = attribute; + + return true; + } + } + + attributeData = null; + return false; + } + } +} \ No newline at end of file diff --git a/BitsKit.Generator/TypeSymbolProcessor.cs b/BitsKit.Generator/TypeSymbolProcessor.cs index 9ca09be..88ee8d5 100644 --- a/BitsKit.Generator/TypeSymbolProcessor.cs +++ b/BitsKit.Generator/TypeSymbolProcessor.cs @@ -84,10 +84,6 @@ public bool ReportCompilationIssues(SourceProductionContext context) { bool hasCompilationIssues = false; - if (DiagnosticValidator.IsNotPartial(context, TypeDeclaration, TypeSymbol.Name) | - DiagnosticValidator.IsNested(context, TypeDeclaration, TypeSymbol.Name)) - hasCompilationIssues = true; - foreach (BitFieldModel field in _fields) { if (field.HasCompilationIssues(context, this)) diff --git a/BitsKit.Tests/GeneratorTests.cs b/BitsKit.Tests/GeneratorTests.cs index 3af0e70..12f7a85 100644 --- a/BitsKit.Tests/GeneratorTests.cs +++ b/BitsKit.Tests/GeneratorTests.cs @@ -833,8 +833,8 @@ public System.Boolean Generated03 return source[eol..].TrimStart(); } - - /// Loads the BitsKit.Generator assembly into the current AppDomain - [BitObject(BitOrder.LeastSignificant)] - private readonly partial struct BitsKitGeneratorStub { } } + +/// Loads the BitsKit.Generator assembly into the current AppDomain +[BitObject(BitOrder.LeastSignificant)] +internal readonly partial struct BitsKitGeneratorStub { } \ No newline at end of file From d5f1b09e4c94ef03bd4f0786097c7e495d2647b4 Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 01:19:54 +0100 Subject: [PATCH 03/15] move ConflictingAccessors and ConflictingSetters diagnostics to an analyser --- BitsKit.Generator/BitFieldAnalyser.cs | 63 +++++++++++++++++++++++ BitsKit.Generator/BitObjectAnalyser.cs | 2 +- BitsKit.Generator/DiagnosticValidator.cs | 35 +------------ BitsKit.Generator/Models/BitFieldModel.cs | 4 +- BitsKit.Generator/SymbolExtensions.cs | 27 +++++++++- BitsKit.Generator/TypeSymbolProcessor.cs | 23 +++++---- 6 files changed, 106 insertions(+), 48 deletions(-) create mode 100644 BitsKit.Generator/BitFieldAnalyser.cs diff --git a/BitsKit.Generator/BitFieldAnalyser.cs b/BitsKit.Generator/BitFieldAnalyser.cs new file mode 100644 index 0000000..fb20312 --- /dev/null +++ b/BitsKit.Generator/BitFieldAnalyser.cs @@ -0,0 +1,63 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace BitsKit.Generator +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class BitFieldAnalyser : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics { get; } = [ + DiagnosticDescriptors.ConflictingAccessors, + DiagnosticDescriptors.ConflictingSetters, + ]; + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(context => + { + var bitFieldAttribute = context.Compilation.GetTypeByMetadataName(StringConstants.BitFieldAttributeFullName); + if (bitFieldAttribute == null) return; + + context.RegisterSymbolAction(context => + { + var fieldSymbol = (IFieldSymbol)context.Symbol; + if (!fieldSymbol.TryGetAttributesWithBaseType(bitFieldAttribute, out var thisAttributes)) + { + return; + } + + foreach (var thisAttribute in thisAttributes) + { + // todo: code doesn't read the processor, just stores. so we don't need to give one + var bitField = TypeSymbolProcessor.CreateBitFieldFromAttribute(thisAttribute, null!); + if (bitField == null) continue; + + var accessorModifiers = bitField.Modifiers & BitFieldModifiers.AccessorMask; + if ((accessorModifiers & (accessorModifiers - 1)) != 0 && + accessorModifiers != BitFieldModifiers.ProtectedInternal && + accessorModifiers != BitFieldModifiers.PrivateProtected) + { + // "protected internal" and "private protected" combos are allowed + + context.ReportDiagnostic( + Diagnostic.Create(DiagnosticDescriptors.ConflictingAccessors, thisAttribute.ApplicationSyntaxReference!.GetSyntax().GetLocation(), fieldSymbol.ContainingType.Name, bitField.Name) + ); + } + + var setterModifiers = bitField.Modifiers & BitFieldModifiers.SetterMask; + if ((setterModifiers & (setterModifiers - 1)) != 0) + { + context.ReportDiagnostic( + Diagnostic.Create(DiagnosticDescriptors.ConflictingSetters, thisAttribute.ApplicationSyntaxReference!.GetSyntax().GetLocation(), fieldSymbol.ContainingType.Name, bitField.Name) + ); + } + } + }, SymbolKind.Field); + }); + } + } +} \ No newline at end of file diff --git a/BitsKit.Generator/BitObjectAnalyser.cs b/BitsKit.Generator/BitObjectAnalyser.cs index 37e2760..6ce4a1f 100644 --- a/BitsKit.Generator/BitObjectAnalyser.cs +++ b/BitsKit.Generator/BitObjectAnalyser.cs @@ -22,7 +22,7 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { - var bitObjectAttribute = context.Compilation.GetTypeByMetadataName("BitsKit.BitFields.BitObjectAttribute"); + var bitObjectAttribute = context.Compilation.GetTypeByMetadataName(StringConstants.BitObjectAttributeFullName); if (bitObjectAttribute == null) return; context.RegisterSymbolAction(context => diff --git a/BitsKit.Generator/DiagnosticValidator.cs b/BitsKit.Generator/DiagnosticValidator.cs index cd226c0..88e0dc1 100644 --- a/BitsKit.Generator/DiagnosticValidator.cs +++ b/BitsKit.Generator/DiagnosticValidator.cs @@ -1,7 +1,4 @@ -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis; using BitsKit.Generator.Models; namespace BitsKit.Generator; @@ -30,36 +27,6 @@ public static bool HasMissingFieldType(SourceProductionContext context, BitField bitField.Name); } - public static bool HasConflictingAccessors(SourceProductionContext context, BitFieldModel bitField, string typeName) - { - BitFieldModifiers modifiers = bitField.Modifiers & BitFieldModifiers.AccessorMask; - - // "protected internal" and "private protected" combos are allowed - if (modifiers is BitFieldModifiers.ProtectedInternal or BitFieldModifiers.PrivateProtected) - return false; - - return (modifiers & (modifiers - 1)) != 0 - && ReportDiagnostic( - context, - DiagnosticDescriptors.ConflictingAccessors, - bitField.BackingField.Locations[0], - typeName, - bitField.Name); - } - - public static bool HasConflictingSetters(SourceProductionContext context, BitFieldModel bitField, string typeName) - { - BitFieldModifiers modifiers = bitField.Modifiers & BitFieldModifiers.SetterMask; - - return (modifiers & (modifiers - 1)) != 0 - && ReportDiagnostic( - context, - DiagnosticDescriptors.ConflictingSetters, - bitField.BackingField.Locations[0], - typeName, - bitField.Name); - } - public static bool IsNotEnumType(SourceProductionContext context, EnumFieldModel enumField, string typeName) { return enumField.EnumType is not { EnumUnderlyingType: { } } diff --git a/BitsKit.Generator/Models/BitFieldModel.cs b/BitsKit.Generator/Models/BitFieldModel.cs index abe97ef..5430b96 100644 --- a/BitsKit.Generator/Models/BitFieldModel.cs +++ b/BitsKit.Generator/Models/BitFieldModel.cs @@ -98,9 +98,7 @@ public void GenerateCSharpSource(StringBuilder sb) /// public virtual bool HasCompilationIssues(SourceProductionContext context, TypeSymbolProcessor processor) { - return DiagnosticValidator.HasMissingFieldType(context, this, processor.TypeSymbol.Name) | - DiagnosticValidator.HasConflictingAccessors(context, this, processor.TypeSymbol.Name) | - DiagnosticValidator.HasConflictingSetters(context, this, processor.TypeSymbol.Name); + return DiagnosticValidator.HasMissingFieldType(context, this, processor.TypeSymbol.Name); } /// diff --git a/BitsKit.Generator/SymbolExtensions.cs b/BitsKit.Generator/SymbolExtensions.cs index aa5aaed..1cfe880 100644 --- a/BitsKit.Generator/SymbolExtensions.cs +++ b/BitsKit.Generator/SymbolExtensions.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; namespace BitsKit.Generator @@ -20,5 +21,29 @@ public static bool TryGetAttributeWithType(this ISymbol symbol, ITypeSymbol type attributeData = null; return false; } + + public static bool TryGetAttributesWithBaseType(this ISymbol symbol, ITypeSymbol typeSymbol, [NotNullWhen(true)] out List? result) + { + result = null; + + foreach (AttributeData attribute in symbol.GetAttributes()) + { + var attributeClass = attribute.AttributeClass!; + do + { + if (SymbolEqualityComparer.Default.Equals(attributeClass, typeSymbol)) + { + result ??= []; + result.Add(attribute); + break; + } + + attributeClass = attributeClass.BaseType; + } while (attributeClass != null); + + } + + return result != null; + } } } \ No newline at end of file diff --git a/BitsKit.Generator/TypeSymbolProcessor.cs b/BitsKit.Generator/TypeSymbolProcessor.cs index 88ee8d5..fe82b6c 100644 --- a/BitsKit.Generator/TypeSymbolProcessor.cs +++ b/BitsKit.Generator/TypeSymbolProcessor.cs @@ -99,15 +99,7 @@ private void CreateBitFieldModels(IFieldSymbol backingField, BackingFieldType ba foreach (AttributeData attribute in backingField.GetAttributes()) { - string? attributeType = attribute.AttributeClass?.ToDisplayString(); - - BitFieldModel? bitField = attributeType switch - { - StringConstants.BitFieldAttributeFullName => new IntegralFieldModel(attribute, this), - StringConstants.BooleanFieldAttributeFullName => new BooleanFieldModel(attribute, this), - StringConstants.EnumFieldAttributeFullName => new EnumFieldModel(attribute, this), - _ => null - }; + BitFieldModel? bitField = CreateBitFieldFromAttribute(attribute, this); if (bitField == null) continue; @@ -139,6 +131,19 @@ private void CreateBitFieldModels(IFieldSymbol backingField, BackingFieldType ba offset += bitField.BitCount; } } + + public static BitFieldModel? CreateBitFieldFromAttribute(AttributeData attribute, TypeSymbolProcessor processor) + { + string? attributeType = attribute.AttributeClass?.ToDisplayString(); + + return attributeType switch + { + StringConstants.BitFieldAttributeFullName => new IntegralFieldModel(attribute, processor), + StringConstants.BooleanFieldAttributeFullName => new BooleanFieldModel(attribute, processor), + StringConstants.EnumFieldAttributeFullName => new EnumFieldModel(attribute, processor), + _ => null + }; + } private bool HasInlineArrayAttribute() { From 23805af69e9390dcf029b9c0e34ccc9a6fe2e339 Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 01:40:05 +0100 Subject: [PATCH 04/15] move EnumTypeExpected diagnostic to an analyser --- .../{ => Analysers}/BitFieldAnalyser.cs | 15 +++++++- .../{ => Analysers}/BitObjectAnalyser.cs | 2 +- BitsKit.Generator/DiagnosticValidator.cs | 11 ------ BitsKit.Generator/Models/EnumFieldModel.cs | 38 ++++++++++++------- 4 files changed, 39 insertions(+), 27 deletions(-) rename BitsKit.Generator/{ => Analysers}/BitFieldAnalyser.cs (78%) rename BitsKit.Generator/{ => Analysers}/BitObjectAnalyser.cs (98%) diff --git a/BitsKit.Generator/BitFieldAnalyser.cs b/BitsKit.Generator/Analysers/BitFieldAnalyser.cs similarity index 78% rename from BitsKit.Generator/BitFieldAnalyser.cs rename to BitsKit.Generator/Analysers/BitFieldAnalyser.cs index fb20312..32bd99d 100644 --- a/BitsKit.Generator/BitFieldAnalyser.cs +++ b/BitsKit.Generator/Analysers/BitFieldAnalyser.cs @@ -1,8 +1,9 @@ using System.Collections.Immutable; +using BitsKit.Generator.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; -namespace BitsKit.Generator +namespace BitsKit.Generator.Analysers { [DiagnosticAnalyzer(LanguageNames.CSharp)] public class BitFieldAnalyser : DiagnosticAnalyzer @@ -10,6 +11,7 @@ public class BitFieldAnalyser : DiagnosticAnalyzer public override ImmutableArray SupportedDiagnostics { get; } = [ DiagnosticDescriptors.ConflictingAccessors, DiagnosticDescriptors.ConflictingSetters, + DiagnosticDescriptors.EnumTypeExpected ]; public override void Initialize(AnalysisContext context) @@ -55,6 +57,17 @@ public override void Initialize(AnalysisContext context) Diagnostic.Create(DiagnosticDescriptors.ConflictingSetters, thisAttribute.ApplicationSyntaxReference!.GetSyntax().GetLocation(), fieldSymbol.ContainingType.Name, bitField.Name) ); } + + if (bitField is EnumFieldModel) + { + var enumFieldAttrModel = new EnumFieldAttributeModel(thisAttribute); + if (enumFieldAttrModel.EnumType != null && enumFieldAttrModel.EnumType.EnumUnderlyingType == null) + { + context.ReportDiagnostic( + Diagnostic.Create(DiagnosticDescriptors.EnumTypeExpected, thisAttribute.ApplicationSyntaxReference!.GetSyntax().GetLocation(), fieldSymbol.ContainingType.Name, bitField.Name) + ); + } + } } }, SymbolKind.Field); }); diff --git a/BitsKit.Generator/BitObjectAnalyser.cs b/BitsKit.Generator/Analysers/BitObjectAnalyser.cs similarity index 98% rename from BitsKit.Generator/BitObjectAnalyser.cs rename to BitsKit.Generator/Analysers/BitObjectAnalyser.cs index 6ce4a1f..9827efd 100644 --- a/BitsKit.Generator/BitObjectAnalyser.cs +++ b/BitsKit.Generator/Analysers/BitObjectAnalyser.cs @@ -5,7 +5,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace BitsKit.Generator +namespace BitsKit.Generator.Analysers { [DiagnosticAnalyzer(LanguageNames.CSharp)] public class BitObjectAnalyser : DiagnosticAnalyzer diff --git a/BitsKit.Generator/DiagnosticValidator.cs b/BitsKit.Generator/DiagnosticValidator.cs index 88e0dc1..b2c6d25 100644 --- a/BitsKit.Generator/DiagnosticValidator.cs +++ b/BitsKit.Generator/DiagnosticValidator.cs @@ -26,15 +26,4 @@ public static bool HasMissingFieldType(SourceProductionContext context, BitField typeName, bitField.Name); } - - public static bool IsNotEnumType(SourceProductionContext context, EnumFieldModel enumField, string typeName) - { - return enumField.EnumType is not { EnumUnderlyingType: { } } - && ReportDiagnostic( - context, - DiagnosticDescriptors.EnumTypeExpected, - enumField.BackingField.Locations[0], - typeName, - enumField.Name); - } } diff --git a/BitsKit.Generator/Models/EnumFieldModel.cs b/BitsKit.Generator/Models/EnumFieldModel.cs index 8aea47c..d04bf60 100644 --- a/BitsKit.Generator/Models/EnumFieldModel.cs +++ b/BitsKit.Generator/Models/EnumFieldModel.cs @@ -1,15 +1,18 @@ -using Microsoft.CodeAnalysis; +using System.IO; +using Microsoft.CodeAnalysis; namespace BitsKit.Generator.Models; /// -/// A model representing an enum bit-field +/// Parsed data from EnumFieldAttribute. Intermediate data only (don't store in incremental pipeline) /// -internal sealed class EnumFieldModel : BitFieldModel +internal class EnumFieldAttributeModel { + public string? Name { get; } public INamedTypeSymbol? EnumType { get; } - - public EnumFieldModel(AttributeData attributeData, TypeSymbolProcessor typeSymbol) : base(attributeData, typeSymbol) + public int BitCount { get; set; } + + public EnumFieldAttributeModel(AttributeData attributeData) { switch(attributeData.ConstructorArguments.Length) { @@ -22,22 +25,29 @@ public EnumFieldModel(AttributeData attributeData, TypeSymbolProcessor typeSymbo EnumType = attributeData.ConstructorArguments[2].Value as INamedTypeSymbol; break; default: - return; + throw new InvalidDataException($"unknown number of enum attribute constructor arguments: {attributeData.ConstructorArguments.Length}"); } + } +} - ReturnType = EnumType?.ToDisplayString(); - FieldType = EnumType?.EnumUnderlyingType?.SpecialType.ToBitFieldType(); +/// +/// A model representing an enum bit-field +/// +internal sealed class EnumFieldModel : BitFieldModel +{ + public EnumFieldModel(AttributeData attributeData, TypeSymbolProcessor typeSymbol) : base(attributeData, typeSymbol) + { + var attributeModel = new EnumFieldAttributeModel(attributeData); + Name = attributeModel.Name!; // todo: the nullability on this is well.. wrong. padding fields have no name + BitCount = attributeModel.BitCount; + + ReturnType = attributeModel.EnumType?.ToDisplayString(); + FieldType = attributeModel.EnumType?.EnumUnderlyingType?.SpecialType.ToBitFieldType(); if (string.IsNullOrEmpty(Name)) FieldType = BitFieldType.Padding; } - public override bool HasCompilationIssues(SourceProductionContext context, TypeSymbolProcessor processor) - { - return DiagnosticValidator.IsNotEnumType(context, this, processor.TypeSymbol.Name) | - base.HasCompilationIssues(context, processor); - } - protected override string GetGetterTemplate() { return string.Format(StringConstants.ExplicitGetterTemplate, GetterSource(), ReturnType); From 32a9cb1afce21657998e9ee4a5ff8d6e57c01f28 Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 01:42:17 +0100 Subject: [PATCH 05/15] disable EmitCompilerGeneratedFiles on generator project itself as it doesn't do anything useful --- BitsKit.Generator/BitsKit.Generator.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/BitsKit.Generator/BitsKit.Generator.csproj b/BitsKit.Generator/BitsKit.Generator.csproj index 7872e93..15dc70b 100644 --- a/BitsKit.Generator/BitsKit.Generator.csproj +++ b/BitsKit.Generator/BitsKit.Generator.csproj @@ -5,8 +5,6 @@ false preview enable - true - Generated true AnyCPU;x86;x64 True From 7e29e339680503140dfaf9b7baac424493bb8364 Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 12:25:56 +0100 Subject: [PATCH 06/15] oops, PolySharp is needed for NotNullWhenAttribute --- BitsKit.Generator/BitsKit.Generator.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BitsKit.Generator/BitsKit.Generator.csproj b/BitsKit.Generator/BitsKit.Generator.csproj index 15dc70b..28cbee0 100644 --- a/BitsKit.Generator/BitsKit.Generator.csproj +++ b/BitsKit.Generator/BitsKit.Generator.csproj @@ -24,6 +24,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From fd83cd869dc8e74f82a80209956cd857e3a48f69 Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 12:26:56 +0100 Subject: [PATCH 07/15] make DebugRoslynComponent profile usable --- BitsKit.Generator/Properties/launchSettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BitsKit.Generator/Properties/launchSettings.json b/BitsKit.Generator/Properties/launchSettings.json index 517c167..3fb17af 100644 --- a/BitsKit.Generator/Properties/launchSettings.json +++ b/BitsKit.Generator/Properties/launchSettings.json @@ -1,8 +1,8 @@ { "profiles": { - "Profile 1": { + "Debug Source Generator - on BitsKit.Tests": { "commandName": "DebugRoslynComponent", - "targetProject": "..\\BitsKit.Generator.Tests\\BitsKit.Generator.Tests.csproj" + "targetProject": "..\\BitsKit.Tests\\BitsKit.Tests.csproj" } } } \ No newline at end of file From def214e334de485f94a755fe1048bc7ec6745eb2 Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 12:31:37 +0100 Subject: [PATCH 08/15] storing BaseNamespaceDeclarationSyntax is illegal --- BitsKit.Generator/BitObjectGenerator.cs | 2 +- BitsKit.Generator/TypeSymbolProcessor.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/BitsKit.Generator/BitObjectGenerator.cs b/BitsKit.Generator/BitObjectGenerator.cs index fed2dd4..19ae076 100644 --- a/BitsKit.Generator/BitObjectGenerator.cs +++ b/BitsKit.Generator/BitObjectGenerator.cs @@ -60,7 +60,7 @@ private static void GenerateSourceCode(SourceProductionContext context, Immutabl // print the current namespace if (namespaceGroup.Key is not null) stringBuilder - .AppendLine($"namespace {namespaceGroup.Key.Name.ToFullString()}") + .AppendLine($"namespace {namespaceGroup.Key}") .AppendLine("{"); foreach (TypeSymbolProcessor processor in namespaceGroup) diff --git a/BitsKit.Generator/TypeSymbolProcessor.cs b/BitsKit.Generator/TypeSymbolProcessor.cs index fe82b6c..bcdb2c4 100644 --- a/BitsKit.Generator/TypeSymbolProcessor.cs +++ b/BitsKit.Generator/TypeSymbolProcessor.cs @@ -12,7 +12,7 @@ internal sealed class TypeSymbolProcessor public INamedTypeSymbol TypeSymbol { get; } public TypeDeclarationSyntax TypeDeclaration { get; } public IReadOnlyList Fields => _fields; - public BaseNamespaceDeclarationSyntax? Namespace { get; } + public string? Namespace { get; } public bool IsInlineArray { get; } private readonly BitOrder _defaultBitOrder; @@ -22,7 +22,10 @@ public TypeSymbolProcessor(INamedTypeSymbol typeSymbol, TypeDeclarationSyntax ty { TypeSymbol = typeSymbol; TypeDeclaration = typeDeclaration; - Namespace = TypeDeclaration.Parent as BaseNamespaceDeclarationSyntax; + + Namespace = typeSymbol.ContainingNamespace.ToDisplayString(new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces)); + if (string.IsNullOrWhiteSpace(Namespace)) Namespace = null; + IsInlineArray = HasInlineArrayAttribute(); _defaultBitOrder = (BitOrder)attribute.ConstructorArguments[0].Value!; From 9b562294f222b03b99f67fde09e0e9a30b00a03b Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 13:00:36 +0100 Subject: [PATCH 09/15] storing TypeDeclarationSyntax is illegal --- BitsKit.Generator/BitObjectGenerator.cs | 2 +- BitsKit.Generator/Models/BitFieldModel.cs | 2 +- BitsKit.Generator/StringConstants.cs | 8 +++---- BitsKit.Generator/TypeSymbolProcessor.cs | 26 ++++++++++++++++------- BitsKit.Tests/GeneratorTests.cs | 16 +++++++------- 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/BitsKit.Generator/BitObjectGenerator.cs b/BitsKit.Generator/BitObjectGenerator.cs index 19ae076..1bb74c5 100644 --- a/BitsKit.Generator/BitObjectGenerator.cs +++ b/BitsKit.Generator/BitObjectGenerator.cs @@ -40,7 +40,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .GetAttributes() .Single(a => a.AttributeClass?.ToDisplayString() == StringConstants.BitObjectAttributeFullName); - return new(typeSymbol, typeDeclaration, attribute); + return new(typeSymbol, attribute); } private static void GenerateSourceCode(SourceProductionContext context, ImmutableArray processors) diff --git a/BitsKit.Generator/Models/BitFieldModel.cs b/BitsKit.Generator/Models/BitFieldModel.cs index 5430b96..a8093c4 100644 --- a/BitsKit.Generator/Models/BitFieldModel.cs +++ b/BitsKit.Generator/Models/BitFieldModel.cs @@ -200,7 +200,7 @@ protected bool IsReadOnly() /// private bool SupportsReadOnlyGetter() { - return TypeSymbol.TypeDeclaration.IsStruct() && + return TypeSymbol.IsStruct && BackingFieldType != BackingFieldType.Pointer && BackingFieldType != BackingFieldType.InlineArray && !IsReadOnly(); diff --git a/BitsKit.Generator/StringConstants.cs b/BitsKit.Generator/StringConstants.cs index 1d77a98..96dc573 100644 --- a/BitsKit.Generator/StringConstants.cs +++ b/BitsKit.Generator/StringConstants.cs @@ -31,13 +31,11 @@ internal static class StringConstants /// /// Template for the type declaration /// - /// {0} = Modifiers
- /// {1} = Keyword
- /// {2} = Record ClassOrStructKeyword
- /// {3} = Identifier + /// {0} = Keyword
+ /// {1} = Identifier ///
///
- public const string TypeDeclarationTemplate = "{0} {1} {2} {3}"; + public const string TypeDeclarationTemplate = "partial {0} {1}"; /// /// Template for a property declaration diff --git a/BitsKit.Generator/TypeSymbolProcessor.cs b/BitsKit.Generator/TypeSymbolProcessor.cs index bcdb2c4..8cedf23 100644 --- a/BitsKit.Generator/TypeSymbolProcessor.cs +++ b/BitsKit.Generator/TypeSymbolProcessor.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis; using System.Text; using BitsKit.Generator.Models; @@ -10,22 +9,35 @@ namespace BitsKit.Generator; internal sealed class TypeSymbolProcessor { public INamedTypeSymbol TypeSymbol { get; } - public TypeDeclarationSyntax TypeDeclaration { get; } public IReadOnlyList Fields => _fields; public string? Namespace { get; } + public bool IsStruct { get; } public bool IsInlineArray { get; } private readonly BitOrder _defaultBitOrder; private readonly List _fields = []; + + private readonly string _syntaxKeyword; + private readonly string _syntaxIdentifier; - public TypeSymbolProcessor(INamedTypeSymbol typeSymbol, TypeDeclarationSyntax typeDeclaration, AttributeData attribute) + public TypeSymbolProcessor(INamedTypeSymbol typeSymbol, AttributeData attribute) { TypeSymbol = typeSymbol; - TypeDeclaration = typeDeclaration; + + _syntaxKeyword = typeSymbol.TypeKind switch + { + TypeKind.Struct when typeSymbol.IsRecord => "record struct", + TypeKind.Struct => "struct", + TypeKind.Interface => "interface", + TypeKind.Class when typeSymbol.IsRecord => "record", + _ => "class" + }; + _syntaxIdentifier = typeSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); Namespace = typeSymbol.ContainingNamespace.ToDisplayString(new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces)); if (string.IsNullOrWhiteSpace(Namespace)) Namespace = null; + IsStruct = typeSymbol.TypeKind == TypeKind.Struct; IsInlineArray = HasInlineArrayAttribute(); _defaultBitOrder = (BitOrder)attribute.ConstructorArguments[0].Value!; @@ -35,10 +47,8 @@ public void GenerateCSharpSource(StringBuilder sb) { sb.AppendIndentedLine(1, StringConstants.TypeDeclarationTemplate, - TypeDeclaration.Modifiers, - TypeDeclaration.Keyword.Text, - (TypeDeclaration as RecordDeclarationSyntax)?.ClassOrStructKeyword.Text, - TypeDeclaration.Identifier.Text) + _syntaxKeyword, + _syntaxIdentifier) .AppendIndentedLine(1, "{"); foreach (BitFieldModel field in _fields) diff --git a/BitsKit.Tests/GeneratorTests.cs b/BitsKit.Tests/GeneratorTests.cs index 12f7a85..5569856 100644 --- a/BitsKit.Tests/GeneratorTests.cs +++ b/BitsKit.Tests/GeneratorTests.cs @@ -242,7 +242,7 @@ public ref partial struct BitFieldReadOnly "; string expected = @" - public ref partial struct BitFieldReadOnly + partial struct BitFieldReadOnly { public Int32 Generated00 { @@ -285,7 +285,7 @@ public readonly ref partial struct BitFieldReadOnly "; string expected = @" - public readonly ref partial struct BitFieldReadOnly + partial struct BitFieldReadOnly { public Int32 Generated00 { @@ -344,7 +344,7 @@ public unsafe partial class BitFieldGeneratorTest "; string expected = @" - public unsafe partial class BitFieldGeneratorTest + partial class BitFieldGeneratorTest { public Int32 Generated01 { @@ -490,7 +490,7 @@ public unsafe partial class BitFieldGeneratorTest "; string expected = @" - public unsafe partial class BitFieldGeneratorTest + partial class BitFieldGeneratorTest { public Int32 Generated01 { @@ -581,7 +581,7 @@ public unsafe ref partial struct BooleanGeneratorTest "; string expected = @" - public unsafe ref partial struct BooleanGeneratorTest + partial struct BooleanGeneratorTest { public System.Boolean Generated01 { @@ -639,7 +639,7 @@ public unsafe ref partial struct EnumGeneratorTest "; string expected = @" - public unsafe ref partial struct EnumGeneratorTest + partial struct EnumGeneratorTest { public BitsKit.Tests.TestEnum Generated00 { @@ -710,7 +710,7 @@ public ref partial struct BitFieldIntegerConversion "; string expected = @" - public ref partial struct BitFieldIntegerConversion + partial struct BitFieldIntegerConversion { public Byte Generated00 { @@ -750,7 +750,7 @@ public partial struct BitFieldInlineArray "; string expected = @" - public partial struct BitFieldInlineArray + partial struct BitFieldInlineArray { public Int32 Generated00 { From 75c4f080da06eb0c88d4d5f7e7d235667e4d43dc Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 14:09:07 +0100 Subject: [PATCH 10/15] storing IFieldSymbol is illegal --- .../Analysers/BitFieldAnalyser.cs | 3 +++ BitsKit.Generator/BitObjectGenerator.cs | 5 ---- BitsKit.Generator/DiagnosticValidator.cs | 4 +-- BitsKit.Generator/Models/BackingFieldModel.cs | 24 ++++++++++++++++++ BitsKit.Generator/Models/BitFieldModel.cs | 24 ++++++------------ BitsKit.Generator/TypeSymbolProcessor.cs | 25 +++++-------------- 6 files changed, 43 insertions(+), 42 deletions(-) create mode 100644 BitsKit.Generator/Models/BackingFieldModel.cs diff --git a/BitsKit.Generator/Analysers/BitFieldAnalyser.cs b/BitsKit.Generator/Analysers/BitFieldAnalyser.cs index 32bd99d..42693c9 100644 --- a/BitsKit.Generator/Analysers/BitFieldAnalyser.cs +++ b/BitsKit.Generator/Analysers/BitFieldAnalyser.cs @@ -19,6 +19,9 @@ public override void Initialize(AnalysisContext context) context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + // todo: reintroduce FieldTypeNotDefined + // but i'm not sure how it was triggered + context.RegisterCompilationStartAction(context => { var bitFieldAttribute = context.Compilation.GetTypeByMetadataName(StringConstants.BitFieldAttributeFullName); diff --git a/BitsKit.Generator/BitObjectGenerator.cs b/BitsKit.Generator/BitObjectGenerator.cs index 1bb74c5..70647e0 100644 --- a/BitsKit.Generator/BitObjectGenerator.cs +++ b/BitsKit.Generator/BitObjectGenerator.cs @@ -69,11 +69,6 @@ private static void GenerateSourceCode(SourceProductionContext context, Immutabl if (processor.EnumerateFields() == 0) continue; - // check and report any compilation issues and prevent - // code generation for this type if there are - if (processor.ReportCompilationIssues(context)) - continue; - processor.GenerateCSharpSource(stringBuilder); } diff --git a/BitsKit.Generator/DiagnosticValidator.cs b/BitsKit.Generator/DiagnosticValidator.cs index b2c6d25..c03d03b 100644 --- a/BitsKit.Generator/DiagnosticValidator.cs +++ b/BitsKit.Generator/DiagnosticValidator.cs @@ -16,7 +16,7 @@ public static bool ReportDiagnostic(SourceProductionContext context, DiagnosticD return descriptor is { DefaultSeverity: DiagnosticSeverity.Error }; } - public static bool HasMissingFieldType(SourceProductionContext context, BitFieldModel bitField, string typeName) + /*public static bool HasMissingFieldType(SourceProductionContext context, BitFieldModel bitField, string typeName) { return bitField.FieldType is null && ReportDiagnostic( @@ -25,5 +25,5 @@ public static bool HasMissingFieldType(SourceProductionContext context, BitField bitField.BackingField.Locations[0], typeName, bitField.Name); - } + }*/ } diff --git a/BitsKit.Generator/Models/BackingFieldModel.cs b/BitsKit.Generator/Models/BackingFieldModel.cs new file mode 100644 index 0000000..2b43ade --- /dev/null +++ b/BitsKit.Generator/Models/BackingFieldModel.cs @@ -0,0 +1,24 @@ +using Microsoft.CodeAnalysis; + +namespace BitsKit.Generator.Models +{ + internal record BackingFieldModel + { + public readonly string Name; + public readonly string TypeString; + public readonly int FixedSize; + public readonly bool IsReadOnly; + + public readonly BackingFieldType Type; + + public BackingFieldModel(IFieldSymbol fieldSymbol, BackingFieldType type) + { + Name = fieldSymbol.Name; + TypeString = fieldSymbol.Type.ToDisplayString(); + FixedSize = fieldSymbol.FixedSize; + IsReadOnly = fieldSymbol.IsReadOnly; + + Type = type; + } + } +} \ No newline at end of file diff --git a/BitsKit.Generator/Models/BitFieldModel.cs b/BitsKit.Generator/Models/BitFieldModel.cs index a8093c4..34a7a71 100644 --- a/BitsKit.Generator/Models/BitFieldModel.cs +++ b/BitsKit.Generator/Models/BitFieldModel.cs @@ -9,8 +9,8 @@ internal abstract class BitFieldModel public string Name { get; set; } = null!; public BitFieldType? FieldType { get; set; } public string? ReturnType { get; set; } - public IFieldSymbol BackingField { get; set; } = null!; - public BackingFieldType BackingFieldType { get; set; } + public BackingFieldModel BackingField { get; set; } = null!; + public BackingFieldType BackingFieldType => BackingField.Type; public int BitOffset { get; set; } public int BitCount { get; set; } public BitOrder BitOrder { get; set; } @@ -54,7 +54,7 @@ public void GenerateCSharpSource(StringBuilder sb) GetPropertyTemplate(), accessor, Modifiers.HasFlag(BitFieldModifiers.Required) ? "required" : "", - ReturnType ?? FieldType.ToString(), + ReturnType ?? FieldType?.ToString(), Name) .AppendIndentedLine(2, "{"); @@ -64,13 +64,13 @@ public void GenerateCSharpSource(StringBuilder sb) GetGetterTemplate(), SupportsReadOnlyGetter() ? "readonly" : "", "get", - FieldType!.Value.ToIntegralName(), + FieldType?.ToIntegralName(), BitOrder.ToShortName(), BackingField.Name, BitOffset, BitCount, BackingField.FixedSize, - BackingField.Type); + BackingField.TypeString); } // setter @@ -80,27 +80,19 @@ public void GenerateCSharpSource(StringBuilder sb) GetSetterTemplate(), "", Modifiers.HasFlag(BitFieldModifiers.InitOnly) ? "init" : "set", - FieldType!.Value.ToIntegralName(), + FieldType?.ToIntegralName(), BitOrder.ToShortName(), BackingField.Name, BitOffset, BitCount, BackingField.FixedSize, - BackingField.Type); + BackingField.TypeString); } sb.AppendIndentedLine(2, "}") .AppendLine(); } - /// - /// Diagnoses if the field will produce non-compilable or erroneous code - /// - public virtual bool HasCompilationIssues(SourceProductionContext context, TypeSymbolProcessor processor) - { - return DiagnosticValidator.HasMissingFieldType(context, this, processor.TypeSymbol.Name); - } - /// /// Generates a template for the property accessors, type and name /// @@ -186,7 +178,7 @@ BackingFieldType.Span or /// protected bool IsReadOnly() { - string backingType = BackingField.Type.ToDisplayString(); + string backingType = BackingField.TypeString; return BackingField.IsReadOnly || backingType == "System.ReadOnlySpan" || diff --git a/BitsKit.Generator/TypeSymbolProcessor.cs b/BitsKit.Generator/TypeSymbolProcessor.cs index 8cedf23..3809e69 100644 --- a/BitsKit.Generator/TypeSymbolProcessor.cs +++ b/BitsKit.Generator/TypeSymbolProcessor.cs @@ -87,26 +87,14 @@ _ when field.Type.IsSupportedIntegralType() => IsInlineArray ? if (backingType == BackingFieldType.Invalid) continue; - CreateBitFieldModels(field, backingType); + var backingModel = new BackingFieldModel(field, backingType); + CreateBitFieldModels(field, backingModel); } return _fields.Count; } - public bool ReportCompilationIssues(SourceProductionContext context) - { - bool hasCompilationIssues = false; - - foreach (BitFieldModel field in _fields) - { - if (field.HasCompilationIssues(context, this)) - hasCompilationIssues = true; - } - - return hasCompilationIssues; - } - - private void CreateBitFieldModels(IFieldSymbol backingField, BackingFieldType backingType) + private void CreateBitFieldModels(IFieldSymbol backingField, BackingFieldModel backingModel) { int offset = 0; @@ -117,8 +105,7 @@ private void CreateBitFieldModels(IFieldSymbol backingField, BackingFieldType ba if (bitField == null) continue; - bitField.BackingField = backingField; - bitField.BackingFieldType = backingType; + bitField.BackingField = backingModel; bitField.BitOffset = offset; bitField.BitOrder = _defaultBitOrder; @@ -130,11 +117,11 @@ private void CreateBitFieldModels(IFieldSymbol backingField, BackingFieldType ba bitField.BitOrder ^= BitOrder.MostSignificant; // integrals inherit their field type from their backing field - if (backingType == BackingFieldType.Integral) + if (backingModel.Type == BackingFieldType.Integral) bitField.FieldType = backingField.Type.SpecialType.ToBitFieldType(); // allow inline arrays to infer their type - if (backingType == BackingFieldType.InlineArray) + if (backingModel.Type == BackingFieldType.InlineArray) bitField.FieldType ??= backingField.Type.SpecialType.ToBitFieldType(); // add to list of fields to generate From e01a6320ea264821b8ffc632c6d67970e943e142 Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 14:14:44 +0100 Subject: [PATCH 11/15] self-referential BitFieldModel->TypeSymbolProcessor is a bad idea --- BitsKit.Generator/Models/BitFieldModel.cs | 18 +++++++++++++----- BitsKit.Generator/Models/BooleanFieldModel.cs | 4 ++-- BitsKit.Generator/Models/EnumFieldModel.cs | 4 ++-- BitsKit.Generator/Models/IntegralFieldModel.cs | 4 ++-- BitsKit.Generator/TypeSymbolProcessor.cs | 7 +++---- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/BitsKit.Generator/Models/BitFieldModel.cs b/BitsKit.Generator/Models/BitFieldModel.cs index 34a7a71..b96f2f1 100644 --- a/BitsKit.Generator/Models/BitFieldModel.cs +++ b/BitsKit.Generator/Models/BitFieldModel.cs @@ -4,7 +4,7 @@ namespace BitsKit.Generator.Models; -internal abstract class BitFieldModel +internal abstract record BitFieldModel { public string Name { get; set; } = null!; public BitFieldType? FieldType { get; set; } @@ -16,11 +16,19 @@ internal abstract class BitFieldModel public BitOrder BitOrder { get; set; } public bool ReverseBitOrder { get; } public BitFieldModifiers Modifiers { get; } - public TypeSymbolProcessor TypeSymbol { get; } + + private readonly bool _containingTypeIsStruct; - public BitFieldModel(AttributeData attributeData, TypeSymbolProcessor typeSymbol) + public BitFieldModel(AttributeData attributeData, TypeSymbolProcessor? typeSymbol) { - TypeSymbol = typeSymbol; + if (typeSymbol != null) + { + // todo: for now, analyser passes null + // these fields don't matter for it + + _containingTypeIsStruct = typeSymbol.IsStruct; + BitOrder = typeSymbol.DefaultBitOrder; + } for (int i = 0; i < attributeData.NamedArguments.Length; i++) { @@ -192,7 +200,7 @@ protected bool IsReadOnly() /// private bool SupportsReadOnlyGetter() { - return TypeSymbol.IsStruct && + return _containingTypeIsStruct && BackingFieldType != BackingFieldType.Pointer && BackingFieldType != BackingFieldType.InlineArray && !IsReadOnly(); diff --git a/BitsKit.Generator/Models/BooleanFieldModel.cs b/BitsKit.Generator/Models/BooleanFieldModel.cs index f09ba99..beda46d 100644 --- a/BitsKit.Generator/Models/BooleanFieldModel.cs +++ b/BitsKit.Generator/Models/BooleanFieldModel.cs @@ -5,9 +5,9 @@ namespace BitsKit.Generator.Models; /// /// A model representing a boolean bit-field /// -internal sealed class BooleanFieldModel : BitFieldModel +internal sealed record BooleanFieldModel : BitFieldModel { - public BooleanFieldModel(AttributeData attributeData, TypeSymbolProcessor typeSymbol) : base(attributeData, typeSymbol) + public BooleanFieldModel(AttributeData attributeData, TypeSymbolProcessor? typeSymbol) : base(attributeData, typeSymbol) { switch (attributeData.ConstructorArguments.Length) { diff --git a/BitsKit.Generator/Models/EnumFieldModel.cs b/BitsKit.Generator/Models/EnumFieldModel.cs index d04bf60..f8f3238 100644 --- a/BitsKit.Generator/Models/EnumFieldModel.cs +++ b/BitsKit.Generator/Models/EnumFieldModel.cs @@ -33,9 +33,9 @@ public EnumFieldAttributeModel(AttributeData attributeData) /// /// A model representing an enum bit-field /// -internal sealed class EnumFieldModel : BitFieldModel +internal sealed record EnumFieldModel : BitFieldModel { - public EnumFieldModel(AttributeData attributeData, TypeSymbolProcessor typeSymbol) : base(attributeData, typeSymbol) + public EnumFieldModel(AttributeData attributeData, TypeSymbolProcessor? typeSymbol) : base(attributeData, typeSymbol) { var attributeModel = new EnumFieldAttributeModel(attributeData); Name = attributeModel.Name!; // todo: the nullability on this is well.. wrong. padding fields have no name diff --git a/BitsKit.Generator/Models/IntegralFieldModel.cs b/BitsKit.Generator/Models/IntegralFieldModel.cs index 98f4e90..2358555 100644 --- a/BitsKit.Generator/Models/IntegralFieldModel.cs +++ b/BitsKit.Generator/Models/IntegralFieldModel.cs @@ -5,7 +5,7 @@ namespace BitsKit.Generator.Models; /// /// A model representing an integral bit-field /// -internal sealed class IntegralFieldModel : BitFieldModel +internal sealed record IntegralFieldModel : BitFieldModel { private bool IsTypeCast => this is { @@ -13,7 +13,7 @@ internal sealed class IntegralFieldModel : BitFieldModel ReturnType.Length: > 0 }; - public IntegralFieldModel(AttributeData attributeData, TypeSymbolProcessor typeSymbol) : base(attributeData, typeSymbol) + public IntegralFieldModel(AttributeData attributeData, TypeSymbolProcessor? typeSymbol) : base(attributeData, typeSymbol) { switch (attributeData.ConstructorArguments.Length) { diff --git a/BitsKit.Generator/TypeSymbolProcessor.cs b/BitsKit.Generator/TypeSymbolProcessor.cs index 3809e69..8237d20 100644 --- a/BitsKit.Generator/TypeSymbolProcessor.cs +++ b/BitsKit.Generator/TypeSymbolProcessor.cs @@ -11,10 +11,11 @@ internal sealed class TypeSymbolProcessor public INamedTypeSymbol TypeSymbol { get; } public IReadOnlyList Fields => _fields; public string? Namespace { get; } + + public BitOrder DefaultBitOrder { get; } public bool IsStruct { get; } public bool IsInlineArray { get; } - private readonly BitOrder _defaultBitOrder; private readonly List _fields = []; private readonly string _syntaxKeyword; @@ -37,10 +38,9 @@ public TypeSymbolProcessor(INamedTypeSymbol typeSymbol, AttributeData attribute) Namespace = typeSymbol.ContainingNamespace.ToDisplayString(new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces)); if (string.IsNullOrWhiteSpace(Namespace)) Namespace = null; + DefaultBitOrder = (BitOrder)attribute.ConstructorArguments[0].Value!; IsStruct = typeSymbol.TypeKind == TypeKind.Struct; IsInlineArray = HasInlineArrayAttribute(); - - _defaultBitOrder = (BitOrder)attribute.ConstructorArguments[0].Value!; } public void GenerateCSharpSource(StringBuilder sb) @@ -107,7 +107,6 @@ private void CreateBitFieldModels(IFieldSymbol backingField, BackingFieldModel b bitField.BackingField = backingModel; bitField.BitOffset = offset; - bitField.BitOrder = _defaultBitOrder; // padding fields are not generated if (bitField is not { FieldType: BitFieldType.Padding }) From 38594a030df0751eb873b61a55b4ad41058e9a76 Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 14:18:45 +0100 Subject: [PATCH 12/15] storing INamedTypeSymbol is illegal --- BitsKit.Generator/BitObjectGenerator.cs | 16 ---------------- BitsKit.Generator/TypeSymbolProcessor.cs | 19 +++++++++---------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/BitsKit.Generator/BitObjectGenerator.cs b/BitsKit.Generator/BitObjectGenerator.cs index 70647e0..a11ca79 100644 --- a/BitsKit.Generator/BitObjectGenerator.cs +++ b/BitsKit.Generator/BitObjectGenerator.cs @@ -19,7 +19,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) StringConstants.BitObjectAttributeFullName, predicate: IsValidTypeDeclaration, transform: ProcessSyntaxNode) - .WithComparer(TypeSymbolProcessorComparer.Default) .Where(x => x is not null)!; var model = typeDeclarations.Collect(); @@ -65,10 +64,6 @@ private static void GenerateSourceCode(SourceProductionContext context, Immutabl foreach (TypeSymbolProcessor processor in namespaceGroup) { - // evaluate if there are actually any valid fields - if (processor.EnumerateFields() == 0) - continue; - processor.GenerateCSharpSource(stringBuilder); } @@ -86,14 +81,3 @@ private static void GenerateSourceCode(SourceProductionContext context, Immutabl private static bool IsValidTypeDeclaration(SyntaxNode node, CancellationToken _) => node is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax; } - -file class TypeSymbolProcessorComparer : IEqualityComparer -{ - public static TypeSymbolProcessorComparer Default { get; } = new(); - - public bool Equals(TypeSymbolProcessor? x, TypeSymbolProcessor? y) => - SymbolEqualityComparer.Default.Equals(x?.TypeSymbol, y?.TypeSymbol); - - public int GetHashCode(TypeSymbolProcessor? obj) => - SymbolEqualityComparer.Default.GetHashCode(obj?.TypeSymbol); -} diff --git a/BitsKit.Generator/TypeSymbolProcessor.cs b/BitsKit.Generator/TypeSymbolProcessor.cs index 8237d20..e5efe3d 100644 --- a/BitsKit.Generator/TypeSymbolProcessor.cs +++ b/BitsKit.Generator/TypeSymbolProcessor.cs @@ -6,10 +6,9 @@ namespace BitsKit.Generator; -internal sealed class TypeSymbolProcessor +internal sealed record TypeSymbolProcessor { - public INamedTypeSymbol TypeSymbol { get; } - public IReadOnlyList Fields => _fields; + public IReadOnlyList Fields => _fields; // todo: eq public string? Namespace { get; } public BitOrder DefaultBitOrder { get; } @@ -23,8 +22,6 @@ internal sealed class TypeSymbolProcessor public TypeSymbolProcessor(INamedTypeSymbol typeSymbol, AttributeData attribute) { - TypeSymbol = typeSymbol; - _syntaxKeyword = typeSymbol.TypeKind switch { TypeKind.Struct when typeSymbol.IsRecord => "record struct", @@ -40,7 +37,9 @@ public TypeSymbolProcessor(INamedTypeSymbol typeSymbol, AttributeData attribute) DefaultBitOrder = (BitOrder)attribute.ConstructorArguments[0].Value!; IsStruct = typeSymbol.TypeKind == TypeKind.Struct; - IsInlineArray = HasInlineArrayAttribute(); + IsInlineArray = HasInlineArrayAttribute(typeSymbol); + + EnumerateFields(typeSymbol); } public void GenerateCSharpSource(StringBuilder sb) @@ -59,11 +58,11 @@ public void GenerateCSharpSource(StringBuilder sb) .AppendLine(); } - public int EnumerateFields() + private int EnumerateFields(ITypeSymbol typeSymbol) { _fields.Clear(); - foreach (IFieldSymbol field in TypeSymbol.GetMembers().OfType()) + foreach (IFieldSymbol field in typeSymbol.GetMembers().OfType()) { if (!IsValidFieldSymbol(field)) continue; @@ -144,9 +143,9 @@ private void CreateBitFieldModels(IFieldSymbol backingField, BackingFieldModel b }; } - private bool HasInlineArrayAttribute() + private static bool HasInlineArrayAttribute(ITypeSymbol typeSymbol) { - return (int?)TypeSymbol + return (int?)typeSymbol .GetAttributes() .FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == StringConstants.InlineArrayAttributeFullName)? .ConstructorArguments[0].Value > 0; From b25fd83724f787cb9551409825774bfd41b93c0f Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 14:21:40 +0100 Subject: [PATCH 13/15] storing List is illegal --- BitsKit.Generator/BitsKit.Generator.csproj | 1 + BitsKit.Generator/EquatableReadOnlyList.cs | 57 ++++++++++++++++++++++ BitsKit.Generator/TypeSymbolProcessor.cs | 20 ++++---- 3 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 BitsKit.Generator/EquatableReadOnlyList.cs diff --git a/BitsKit.Generator/BitsKit.Generator.csproj b/BitsKit.Generator/BitsKit.Generator.csproj index 28cbee0..909e193 100644 --- a/BitsKit.Generator/BitsKit.Generator.csproj +++ b/BitsKit.Generator/BitsKit.Generator.csproj @@ -22,6 +22,7 @@ + diff --git a/BitsKit.Generator/EquatableReadOnlyList.cs b/BitsKit.Generator/EquatableReadOnlyList.cs new file mode 100644 index 0000000..29684b9 --- /dev/null +++ b/BitsKit.Generator/EquatableReadOnlyList.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace BitsKit.Generator +{ + [ExcludeFromCodeCoverage] + public static class EquatableReadOnlyList + { + public static EquatableReadOnlyList ToEquatableReadOnlyList(this IEnumerable enumerable) + => new(enumerable is IReadOnlyList l ? l : [.. enumerable]); + } + + /// + /// A wrapper for IReadOnlyList that provides value equality support for the wrapped list. + /// + [ExcludeFromCodeCoverage] + public readonly struct EquatableReadOnlyList( + IReadOnlyList? collection + ) : IEquatable>, IReadOnlyList + { + private IReadOnlyList Collection => collection ?? []; + + public bool Equals(EquatableReadOnlyList other) + => this.SequenceEqual(other); + + public override bool Equals(object? obj) + => obj is EquatableReadOnlyList other && Equals(other); + + public override int GetHashCode() + { + var hashCode = new HashCode(); + + foreach (var item in Collection) + hashCode.Add(item); + + return hashCode.ToHashCode(); + } + + IEnumerator IEnumerable.GetEnumerator() + => Collection.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => Collection.GetEnumerator(); + + public int Count => Collection.Count; + public T this[int index] => Collection[index]; + + public static bool operator ==(EquatableReadOnlyList left, EquatableReadOnlyList right) + => left.Equals(right); + + public static bool operator !=(EquatableReadOnlyList left, EquatableReadOnlyList right) + => !left.Equals(right); + } +} \ No newline at end of file diff --git a/BitsKit.Generator/TypeSymbolProcessor.cs b/BitsKit.Generator/TypeSymbolProcessor.cs index e5efe3d..d5ec8a9 100644 --- a/BitsKit.Generator/TypeSymbolProcessor.cs +++ b/BitsKit.Generator/TypeSymbolProcessor.cs @@ -8,14 +8,12 @@ namespace BitsKit.Generator; internal sealed record TypeSymbolProcessor { - public IReadOnlyList Fields => _fields; // todo: eq + public EquatableReadOnlyList Fields { get; } public string? Namespace { get; } public BitOrder DefaultBitOrder { get; } public bool IsStruct { get; } public bool IsInlineArray { get; } - - private readonly List _fields = []; private readonly string _syntaxKeyword; private readonly string _syntaxIdentifier; @@ -39,7 +37,7 @@ public TypeSymbolProcessor(INamedTypeSymbol typeSymbol, AttributeData attribute) IsStruct = typeSymbol.TypeKind == TypeKind.Struct; IsInlineArray = HasInlineArrayAttribute(typeSymbol); - EnumerateFields(typeSymbol); + Fields = EnumerateFields(typeSymbol); } public void GenerateCSharpSource(StringBuilder sb) @@ -50,7 +48,7 @@ public void GenerateCSharpSource(StringBuilder sb) _syntaxIdentifier) .AppendIndentedLine(1, "{"); - foreach (BitFieldModel field in _fields) + foreach (BitFieldModel field in Fields) field.GenerateCSharpSource(sb); sb.RemoveLastLine() @@ -58,9 +56,9 @@ public void GenerateCSharpSource(StringBuilder sb) .AppendLine(); } - private int EnumerateFields(ITypeSymbol typeSymbol) + private EquatableReadOnlyList EnumerateFields(ITypeSymbol typeSymbol) { - _fields.Clear(); + var output = new List(); foreach (IFieldSymbol field in typeSymbol.GetMembers().OfType()) { @@ -87,13 +85,13 @@ _ when field.Type.IsSupportedIntegralType() => IsInlineArray ? continue; var backingModel = new BackingFieldModel(field, backingType); - CreateBitFieldModels(field, backingModel); + CreateBitFieldModels(output, field, backingModel); } - return _fields.Count; + return output.ToEquatableReadOnlyList(); } - private void CreateBitFieldModels(IFieldSymbol backingField, BackingFieldModel backingModel) + private void CreateBitFieldModels(List output, IFieldSymbol backingField, BackingFieldModel backingModel) { int offset = 0; @@ -123,7 +121,7 @@ private void CreateBitFieldModels(IFieldSymbol backingField, BackingFieldModel b bitField.FieldType ??= backingField.Type.SpecialType.ToBitFieldType(); // add to list of fields to generate - _fields.Add(bitField); + output.Add(bitField); } offset += bitField.BitCount; From 929cf6cdd08a1b50d9bed672e9891428ffaba46a Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 16:25:13 +0100 Subject: [PATCH 14/15] add test that validates generator incremental behaviour --- BitsKit.Generator/BitObjectGenerator.cs | 3 +- BitsKit.Tests/GeneratorTests.cs | 104 +++++++++++++++++------- 2 files changed, 75 insertions(+), 32 deletions(-) diff --git a/BitsKit.Generator/BitObjectGenerator.cs b/BitsKit.Generator/BitObjectGenerator.cs index a11ca79..f3827ee 100644 --- a/BitsKit.Generator/BitObjectGenerator.cs +++ b/BitsKit.Generator/BitObjectGenerator.cs @@ -19,7 +19,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context) StringConstants.BitObjectAttributeFullName, predicate: IsValidTypeDeclaration, transform: ProcessSyntaxNode) - .Where(x => x is not null)!; + .Where(x => x is not null) + .WithTrackingName("Main")!; var model = typeDeclarations.Collect(); context.RegisterSourceOutput(model, GenerateSourceCode); diff --git a/BitsKit.Tests/GeneratorTests.cs b/BitsKit.Tests/GeneratorTests.cs index 5569856..62aa4d4 100644 --- a/BitsKit.Tests/GeneratorTests.cs +++ b/BitsKit.Tests/GeneratorTests.cs @@ -261,7 +261,7 @@ public Int32 Generated20 } "; - string? sourceOutput = GenerateSourceAndTest(source, new BitObjectGenerator()); + string? sourceOutput = GenerateSourceAndTest(source); Assert.IsTrue(Helpers.StrEqualExWhiteSpace(sourceOutput, expected)); } @@ -304,7 +304,7 @@ public Int32 Generated20 } "; - string? sourceOutput = GenerateSourceAndTest(source, new BitObjectGenerator()); + string? sourceOutput = GenerateSourceAndTest(source); Assert.IsTrue(Helpers.StrEqualExWhiteSpace(sourceOutput, expected)); } @@ -461,7 +461,7 @@ public UIntPtr Generated19 } "; - string? sourceOutput = GenerateSourceAndTest(source, new BitObjectGenerator()); + string? sourceOutput = GenerateSourceAndTest(source); Assert.IsTrue(Helpers.StrEqualExWhiteSpace(sourceOutput, expected)); } @@ -553,7 +553,7 @@ private protected Int32 Generated0A } "; - string? sourceOutput = GenerateSourceAndTest(source, new BitObjectGenerator()); + string? sourceOutput = GenerateSourceAndTest(source); Assert.IsTrue(Helpers.StrEqualExWhiteSpace(sourceOutput, expected)); #endif @@ -608,7 +608,7 @@ public unsafe System.Boolean Generated30 } "; - string? sourceOutput = GenerateSourceAndTest(source, new BitObjectGenerator()); + string? sourceOutput = GenerateSourceAndTest(source); Assert.IsTrue(Helpers.StrEqualExWhiteSpace(sourceOutput, expected)); } @@ -689,7 +689,7 @@ public unsafe BitsKit.Tests.TestEnum Generated31 } "; - string? sourceOutput = GenerateSourceAndTest(source, new BitObjectGenerator()); + string? sourceOutput = GenerateSourceAndTest(source); Assert.IsTrue(Helpers.StrEqualExWhiteSpace(sourceOutput, expected)); } @@ -726,7 +726,7 @@ public Int32 Generated10 } "; - string? sourceOutput = GenerateSourceAndTest(source, new BitObjectGenerator()); + string? sourceOutput = GenerateSourceAndTest(source); Assert.IsTrue(Helpers.StrEqualExWhiteSpace(sourceOutput, expected)); } @@ -778,51 +778,93 @@ public System.Boolean Generated03 } "; - string? sourceOutput = GenerateSourceAndTest(source, new BitObjectGenerator()); + string? sourceOutput = GenerateSourceAndTest(source); Assert.IsTrue(Helpers.StrEqualExWhiteSpace(sourceOutput, expected)); } #endif - private static string? GenerateSourceAndTest(string source, IIncrementalGenerator generator) + private static string? GenerateSourceAndTest(string source) { var references = AppDomain.CurrentDomain.GetAssemblies() .Where(assembly => !assembly.IsDynamic) .Select(assembly => MetadataReference.CreateFromFile(assembly.Location)) .Cast(); - CSharpCompilation compilation = CSharpCompilation.Create("compilation", - [CSharpSyntaxTree.ParseText(Helpers.GeneratorTestHeader + source)], - references, - new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true)); - - GeneratorDriver driver = CSharpGeneratorDriver - .Create(generator) - .RunGeneratorsAndUpdateCompilation(compilation, out Compilation? outputCompilation, out ImmutableArray diagnostics); - - var diag = outputCompilation.GetDiagnostics(); - - Assert.IsTrue(diagnostics.IsEmpty); // there were no diagnostics created by the generators - Assert.AreEqual(outputCompilation.SyntaxTrees.Count(), 2); // we have two syntax trees, the original 'user' provided one, and the one added by the generator - Assert.IsTrue(outputCompilation.GetDiagnostics().IsEmpty); // verify the compilation with the added source has no diagnostics - - GeneratorDriverRunResult runResult = driver.GetRunResult(); - - Assert.AreEqual(runResult.GeneratedTrees.Length, 1); - Assert.IsTrue(runResult.Diagnostics.IsEmpty); + CSharpCompilation compilation = CSharpCompilation.Create( + assemblyName: "BitsKit.Tests.InMemory", + syntaxTrees: [CSharpSyntaxTree.ParseText(Helpers.GeneratorTestHeader + source)], + references: references, + options: new CSharpCompilationOptions( + OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true + ) + ); + + var insignificantEditComp = compilation.Clone() + .AddSyntaxTrees(CSharpSyntaxTree.ParseText("// dummy")); + + GeneratorDriver driver = CSharpGeneratorDriver.Create( + generators: [new BitObjectGenerator().AsSourceGenerator()], + driverOptions: new GeneratorDriverOptions(default, trackIncrementalGeneratorSteps: true)); + + var run1Result = RunGenerator(ref driver, compilation); + var run2Result = RunGenerator(ref driver, insignificantEditComp); + + foreach (var outputStep in run2Result.Results[0].TrackedOutputSteps) + { + AssertGeneratorDidntRun(outputStep.Value); + } + AssertGeneratorDidntRun(run2Result.Results[0].TrackedSteps["Main"]); + + Assert.AreEqual(run1Result.GeneratedTrees.Length, 1); + Assert.IsTrue(run1Result.Diagnostics.IsEmpty); - GeneratorRunResult generatorResult = runResult.Results[0]; + GeneratorRunResult generatorResult = run1Result.Results[0]; Assert.AreEqual(generatorResult.Generator.GetGeneratorType(), typeof(BitObjectGenerator)); Assert.IsTrue(generatorResult.Diagnostics.IsEmpty); Assert.AreEqual(generatorResult.GeneratedSources.Length, 1); Assert.IsTrue(generatorResult.Exception is null); string sourceOutput = generatorResult.GeneratedSources[0].SourceText.ToString(); - return TruncateUsings(sourceOutput); } - + + private static GeneratorDriverRunResult RunGenerator( + ref GeneratorDriver driver, + Compilation compilation + ) + { + driver = driver + .RunGeneratorsAndUpdateCompilation( + compilation, + out var outputCompilation, + out var diagnostics + ); + + // verify the compilation with the added source has no diagnostics + Assert.IsFalse( + outputCompilation + .GetDiagnostics() + .Any(d => d.Severity is DiagnosticSeverity.Error or DiagnosticSeverity.Warning) + ); + + // there were no diagnostics created by the generators + Assert.IsTrue(diagnostics.IsEmpty); + + return driver.GetRunResult(); + } + + private static void AssertGeneratorDidntRun(ImmutableArray steps) + { + var outputs = steps.SelectMany(o => o.Outputs); + foreach (var output in outputs) + { + Assert.IsTrue(output.Reason == IncrementalStepRunReason.Unchanged || + output.Reason == IncrementalStepRunReason.Cached); + } + } + private static string? TruncateUsings(string? source) { if (string.IsNullOrEmpty(source)) From 066d4033a279c4519cd3e5add2356375df9a771b Mon Sep 17 00:00:00 2001 From: zingballyhoo Date: Sat, 27 Sep 2025 16:29:30 +0100 Subject: [PATCH 15/15] don't use LangVersion:preview as it differers per machine --- BitsKit.Benchmarks/BitsKit.Benchmarks.csproj | 2 -- BitsKit.Generator/BitsKit.Generator.csproj | 3 --- BitsKit.Tests/BitsKit.Tests.csproj | 3 --- BitsKit/BitsKit.csproj | 3 --- Directory.Build.props | 2 +- 5 files changed, 1 insertion(+), 12 deletions(-) diff --git a/BitsKit.Benchmarks/BitsKit.Benchmarks.csproj b/BitsKit.Benchmarks/BitsKit.Benchmarks.csproj index efb4597..d4b0263 100644 --- a/BitsKit.Benchmarks/BitsKit.Benchmarks.csproj +++ b/BitsKit.Benchmarks/BitsKit.Benchmarks.csproj @@ -4,9 +4,7 @@ Exe net6.0;net7.0;net8.0 enable - enable BitsKit.Benchmarks.Program - AnyCPU;x86;x64 diff --git a/BitsKit.Generator/BitsKit.Generator.csproj b/BitsKit.Generator/BitsKit.Generator.csproj index 909e193..dc5f7b5 100644 --- a/BitsKit.Generator/BitsKit.Generator.csproj +++ b/BitsKit.Generator/BitsKit.Generator.csproj @@ -3,10 +3,7 @@ netstandard2.0 false - preview - enable true - AnyCPU;x86;x64 True True diff --git a/BitsKit.Tests/BitsKit.Tests.csproj b/BitsKit.Tests/BitsKit.Tests.csproj index ca31b2e..da39c34 100644 --- a/BitsKit.Tests/BitsKit.Tests.csproj +++ b/BitsKit.Tests/BitsKit.Tests.csproj @@ -2,12 +2,9 @@ net6.0;net7.0;net8.0 - enable false - AnyCPU;x86;x64 true True - preview diff --git a/BitsKit/BitsKit.csproj b/BitsKit/BitsKit.csproj index 5a78919..d41796d 100644 --- a/BitsKit/BitsKit.csproj +++ b/BitsKit/BitsKit.csproj @@ -3,10 +3,7 @@ netstandard2.1;net6.0;net7.0;net8.0 enable - enable - AnyCPU;x86;x64 True - preview true diff --git a/Directory.Build.props b/Directory.Build.props index 2ab06a2..4e725a0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -22,7 +22,7 @@ enable - preview + 12