Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions .github/workflows/dependency-review.yml

This file was deleted.

2 changes: 0 additions & 2 deletions BitsKit.Benchmarks/BitsKit.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<StartupObject>BitsKit.Benchmarks.Program</StartupObject>
<Platforms>AnyCPU;x86;x64</Platforms>
</PropertyGroup>

<ItemGroup>
Expand Down
8 changes: 4 additions & 4 deletions BitsKit.Benchmarks/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -278,12 +278,12 @@
"bitskit.tests": {
"type": "Project",
"dependencies": {
"BitsKit.Generator": "[1.2.0, )",
"BitsKit.Generator": "[1.3.0, )",
"MSTest.TestAdapter": "[3.5.0, )",
"MSTest.TestFramework": "[3.5.0, )",
"Microsoft.CodeAnalysis.CSharp": "[4.10.0, )",
"Microsoft.NET.Test.Sdk": "[17.10.0, )",
"RejectKid.BitsKit": "[1.2.0, )"
"RejectKid.BitsKit": "[1.3.0, )"
}
},
"RejectKid.BitsKit": {
Expand Down Expand Up @@ -567,12 +567,12 @@
"bitskit.tests": {
"type": "Project",
"dependencies": {
"BitsKit.Generator": "[1.2.0, )",
"BitsKit.Generator": "[1.3.0, )",
"MSTest.TestAdapter": "[3.5.0, )",
"MSTest.TestFramework": "[3.5.0, )",
"Microsoft.CodeAnalysis.CSharp": "[4.10.0, )",
"Microsoft.NET.Test.Sdk": "[17.10.0, )",
"RejectKid.BitsKit": "[1.2.0, )"
"RejectKid.BitsKit": "[1.3.0, )"
}
},
"RejectKid.BitsKit": {
Expand Down
110 changes: 110 additions & 0 deletions BitsKit.Generator/Analysers/BitFieldAnalyser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System.Collections.Immutable;
using BitsKit.Generator.Models;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

namespace BitsKit.Generator.Analysers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class BitFieldAnalyser : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
[
DiagnosticDescriptors.FieldTypeNotDefined,
DiagnosticDescriptors.ConflictingAccessors,
DiagnosticDescriptors.ConflictingSetters,
DiagnosticDescriptors.EnumTypeExpected
];

public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

context.RegisterCompilationStartAction(startContext =>
{
INamedTypeSymbol? bitFieldAttribute = startContext.Compilation.GetTypeByMetadataName(
StringConstants.BitFieldAttributeFullName);

if (bitFieldAttribute is null)
return;

startContext.RegisterSymbolAction(
symbolContext => AnalyzeField(symbolContext, bitFieldAttribute),
SymbolKind.Field);
});
}

private static void AnalyzeField(SymbolAnalysisContext context, ITypeSymbol bitFieldAttribute)
{
if (context.Symbol is not IFieldSymbol fieldSymbol ||
!fieldSymbol.TryGetAttributesWithBaseType(bitFieldAttribute, out var attributes))
{
return;
}

foreach (AttributeData attribute in attributes)
{
BitFieldModel? bitField = TypeSymbolProcessor.CreateBitFieldFromAttribute(attribute, null);
if (bitField is null)
continue;

Location location = attribute.ApplicationSyntaxReference?
.GetSyntax(context.CancellationToken)
.GetLocation() ?? fieldSymbol.Locations[0];

if (bitField is IntegralFieldModel { FieldType: null } && RequiresExplicitFieldType(fieldSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(
DiagnosticDescriptors.FieldTypeNotDefined,
location,
fieldSymbol.ContainingType.Name,
bitField.Name));
}

BitFieldModifiers accessorModifiers = bitField.Modifiers & BitFieldModifiers.AccessorMask;
if ((accessorModifiers & (accessorModifiers - 1)) != 0 &&
accessorModifiers != BitFieldModifiers.ProtectedInternal &&
accessorModifiers != BitFieldModifiers.PrivateProtected)
{
context.ReportDiagnostic(Diagnostic.Create(
DiagnosticDescriptors.ConflictingAccessors,
location,
fieldSymbol.ContainingType.Name,
bitField.Name));
}

BitFieldModifiers setterModifiers = bitField.Modifiers & BitFieldModifiers.SetterMask;
if ((setterModifiers & (setterModifiers - 1)) != 0)
{
context.ReportDiagnostic(Diagnostic.Create(
DiagnosticDescriptors.ConflictingSetters,
location,
fieldSymbol.ContainingType.Name,
bitField.Name));
}

if (bitField is EnumFieldModel)
{
var enumField = new EnumFieldAttributeModel(attribute);
if (enumField.EnumType is { EnumUnderlyingType: null })
{
context.ReportDiagnostic(Diagnostic.Create(
DiagnosticDescriptors.EnumTypeExpected,
location,
fieldSymbol.ContainingType.Name,
bitField.Name));
}
}
}
}

private static bool RequiresExplicitFieldType(IFieldSymbol fieldSymbol) =>
fieldSymbol.Type.ToDisplayString() is
"System.Memory<byte>" or
"System.ReadOnlyMemory<byte>" or
"System.Span<byte>" or
"System.ReadOnlySpan<byte>" or
"byte[]" or
"byte*";
}
59 changes: 59 additions & 0 deletions BitsKit.Generator/Analysers/BitObjectAnalyser.cs
Original file line number Diff line number Diff line change
@@ -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.Analysers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class BitObjectAnalyser : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> 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(StringConstants.BitObjectAttributeFullName);
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);
});
}
}
}
39 changes: 8 additions & 31 deletions BitsKit.Generator/BitObjectGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
StringConstants.BitObjectAttributeFullName,
predicate: IsValidTypeDeclaration,
transform: ProcessSyntaxNode)
.WithComparer(TypeSymbolProcessorComparer.Default)
.Where(x => x is not null)!;

IncrementalValueProvider<(Compilation, ImmutableArray<TypeSymbolProcessor>)> model = context
.CompilationProvider
.Combine(typeDeclarations.Collect());
.Where(x => x is not null)
.WithTrackingName("Main")!;

var model = typeDeclarations.Collect();
context.RegisterSourceOutput(model, GenerateSourceCode);
}

Expand All @@ -43,18 +40,18 @@ 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, (Compilation _, ImmutableArray<TypeSymbolProcessor> Processors) result)
private static void GenerateSourceCode(SourceProductionContext context, ImmutableArray<TypeSymbolProcessor> 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)
{
Expand All @@ -63,20 +60,11 @@ private static void GenerateSourceCode(SourceProductionContext context, (Compila
// 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)
{
// evaluate if there are actually any valid fields
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);
}

Expand All @@ -94,14 +82,3 @@ private static void GenerateSourceCode(SourceProductionContext context, (Compila
private static bool IsValidTypeDeclaration(SyntaxNode node, CancellationToken _) =>
node is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax;
}

file class TypeSymbolProcessorComparer : IEqualityComparer<TypeSymbolProcessor?>
{
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);
}
9 changes: 4 additions & 5 deletions BitsKit.Generator/BitsKit.Generator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
<LangVersion>12.0</LangVersion>
<Nullable>enable</Nullable>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
<IsRoslynComponent>true</IsRoslynComponent>
<Platforms>AnyCPU;x86;x64</Platforms>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<EnforceExtendedAnalyzerRules>True</EnforceExtendedAnalyzerRules>
</PropertyGroup>
Expand All @@ -26,6 +21,10 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
<PackageReference Include="PolySharp" Version="1.15.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
Expand Down
Loading
Loading