From da32bd5957d956b1243646bd48d9305d1c207ebb Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Mon, 20 Jan 2025 00:47:51 +0100 Subject: [PATCH] rough outline Signed-off-by: Timothy Schenk --- .../IntermediateHandlerAndStructTuple.cs | 5 + .../IntermediatePacketHandlerData.cs | 15 -- .../IntermediatePacketStructData.cs | 12 ++ .../PacketMediatorGenerator.cs | 161 +++++++++--------- .../RaiNote.PacketMediator.csproj | 8 + .../RaiNote.PacketMediator.csproj.DotSettings | 2 + 6 files changed, 110 insertions(+), 93 deletions(-) create mode 100644 RaiNote.PacketMediator/IntermediateHandlerAndStructTuple.cs create mode 100644 RaiNote.PacketMediator/IntermediatePacketStructData.cs create mode 100644 RaiNote.PacketMediator/RaiNote.PacketMediator.csproj.DotSettings diff --git a/RaiNote.PacketMediator/IntermediateHandlerAndStructTuple.cs b/RaiNote.PacketMediator/IntermediateHandlerAndStructTuple.cs new file mode 100644 index 0000000..a4e56af --- /dev/null +++ b/RaiNote.PacketMediator/IntermediateHandlerAndStructTuple.cs @@ -0,0 +1,5 @@ +namespace RaiNote.PacketMediator; + +internal record IntermediateHandlerAndStructTuple( + IntermediatePacketHandlerData HandlerData, + IntermediatePacketStructData StructData); \ No newline at end of file diff --git a/RaiNote.PacketMediator/IntermediatePacketHandlerData.cs b/RaiNote.PacketMediator/IntermediatePacketHandlerData.cs index e40b3a2..24b909a 100644 --- a/RaiNote.PacketMediator/IntermediatePacketHandlerData.cs +++ b/RaiNote.PacketMediator/IntermediatePacketHandlerData.cs @@ -1,7 +1,5 @@ // Licensed to Timothy Schenk under the Apache 2.0 License. -using Microsoft.CodeAnalysis; - namespace RaiNote.PacketMediator; internal class IntermediatePacketHandlerData { @@ -14,16 +12,3 @@ internal class IntermediatePacketHandlerData { public string PacketHandlerIdentifier { get; set; } public IntermediatePacketStructHandlerData? PacketStructHandlerData { get; set; } } - -internal record IntermediatePacketStructData( - Location SymbolLocation, - string PacketStructFullIdentifier, - string EnumValue, - string EnumTypeFullIdentifier, - string EnumMemberIdentifier, - string EnumMaxValue, - bool ImplementsInterface); - -internal record IntermediateHandlerAndStructTuple( - IntermediatePacketHandlerData HandlerData, - IntermediatePacketStructData StructData); diff --git a/RaiNote.PacketMediator/IntermediatePacketStructData.cs b/RaiNote.PacketMediator/IntermediatePacketStructData.cs new file mode 100644 index 0000000..a9bbc4a --- /dev/null +++ b/RaiNote.PacketMediator/IntermediatePacketStructData.cs @@ -0,0 +1,12 @@ +using Microsoft.CodeAnalysis; + +namespace RaiNote.PacketMediator; + +internal record IntermediatePacketStructData( + Location SymbolLocation, + string PacketStructFullIdentifier, + string? EnumValue, + string EnumTypeFullIdentifier, + string EnumMemberIdentifier, + string? EnumMaxValue, + bool ImplementsInterface); \ No newline at end of file diff --git a/RaiNote.PacketMediator/PacketMediatorGenerator.cs b/RaiNote.PacketMediator/PacketMediatorGenerator.cs index 16919bc..ddfa143 100644 --- a/RaiNote.PacketMediator/PacketMediatorGenerator.cs +++ b/RaiNote.PacketMediator/PacketMediatorGenerator.cs @@ -88,69 +88,7 @@ public class PacketMediatorGenerator : IIncrementalGenerator { var structsWithAttributes = context.SyntaxProvider .CreateSyntaxProvider( predicate: (node, _) => node is StructDeclarationSyntax { AttributeLists.Count: > 0 }, - transform: (syntaxContext, cancellationToken) => { - var structDeclaration = (StructDeclarationSyntax)syntaxContext.Node; - var model = syntaxContext.SemanticModel; - var symbol = - ModelExtensions.GetDeclaredSymbol(model, structDeclaration, - cancellationToken: cancellationToken) as - INamedTypeSymbol; - var requiredInterfaces = new[] { - "IPacket", "IIncomingPacket", "IOutgoingPacket", "IBidirectionalPacket" - }; - var implementsInterface = symbol != null && symbol.AllInterfaces - .Any(i => requiredInterfaces.Contains(i.Name, StringComparer.Ordinal)); - if (!implementsInterface) { - // TODO: https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.cookbook.md#issue-diagnostics - // or Analyzer - var diagnostic = Diagnostic.Create(_rpmGen001Diagnostic, symbol?.Locations.First(), - symbol?.ToDisplayString()); - } - - // Check for the marker attribute - var attribute = symbol?.GetAttributes() - .FirstOrDefault(attr => { - var attrClass = attr.AttributeClass; - while (attrClass != null) { - if (string.Equals(attrClass.Name, "PacketIdAttribute", StringComparison.Ordinal) && - string.Equals(attrClass.ContainingNamespace.ToDisplayString(), - "RaiNote.PacketMediator", StringComparison.Ordinal)) { - return true; - } - - attrClass = attrClass.BaseType; - } - - return false; - }); - if (attribute == null) { - return null; - } - - var attributeConstructorArgument = attribute.ConstructorArguments[0]; - var enumType = attributeConstructorArgument.Type; - var enumValue = attributeConstructorArgument.Value; - - var enumMember = enumType?.GetMembers() - .OfType() - .FirstOrDefault(f => f.ConstantValue?.Equals(enumValue) == true); - - var enumMaxValue = enumType?.GetMembers() - .OfType().Max(x => x.ConstantValue); - - if (symbol == null || enumMember == null || enumMaxValue == null || enumType == null || - enumValue == null) - return null; - - var intermediatePacketStructData = new IntermediatePacketStructData(symbol.Locations.First(), - symbol.ToDisplayString(), - enumValue.ToString(), - enumType.ToDisplayString(), - enumMember.ToDisplayString(), - enumMaxValue.ToString(), implementsInterface); - - return intermediatePacketStructData; - }) + transform: TransformPacketStructs) .Where(result => result != null); var packetHandlerValues = context.SyntaxProvider.CreateSyntaxProvider( @@ -170,9 +108,11 @@ public class PacketMediatorGenerator : IIncrementalGenerator { if (handlerData == null) return null; var structs = structDatas.Where(sData => { + if (handlerData.PacketStructHandlerData == null) + return false; var equals = sData != null && sData.PacketStructFullIdentifier.Equals(handlerData.PacketStructHandlerData - ?.PacketStructFullIdentifier); + .PacketStructFullIdentifier, StringComparison.Ordinal); return equals; }).FirstOrDefault(); @@ -188,7 +128,8 @@ public class PacketMediatorGenerator : IIncrementalGenerator { // Collect and generate the dictionary context.RegisterSourceOutput(combinedResults, (ctx, result) => { - var combinedInfo = result.ToList(); + var combinedInfo = result.Where(x => x != null).Select(IntermediateHandlerAndStructTuple (x) => x!) + .ToList(); if (combinedInfo.Count <= 0) return; @@ -198,7 +139,8 @@ public class PacketMediatorGenerator : IIncrementalGenerator { var intermediatePacketStructData = combinedInfo.First()?.StructData; if (intermediatePacketStructData?.EnumMaxValue == null) return; - var highestValue = long.Parse(intermediatePacketStructData.EnumMaxValue); + var highestValue = long.Parse(intermediatePacketStructData.EnumMaxValue, NumberStyles.Integer, + new NumberFormatInfo()); var ms = new MemoryStream(); var sw = new StreamWriter(ms, Encoding.UTF8); sw.AutoFlush = true; @@ -216,11 +158,13 @@ public class PacketMediatorGenerator : IIncrementalGenerator { { """); - var valueTuples = combinedInfo.Select((value, i) => (value, i)); - foreach (var ((handlerData, packetStructData), i) in valueTuples) { - var tempVal = long.Parse(packetStructData.EnumValue, - new NumberFormatInfo()); - usedValues.Add(tempVal); + foreach (var (handlerData, packetStructData) in combinedInfo) { + if (packetStructData.EnumValue != null) { + var tempVal = long.Parse(packetStructData.EnumValue, + new NumberFormatInfo()); + usedValues.Add(tempVal); + } + sw.WriteLine($""" case {packetStructData.EnumMemberIdentifier}: var packet = new {handlerData.PacketHandlerIdentifier}(); @@ -279,6 +223,67 @@ public class PacketMediatorGenerator : IIncrementalGenerator { }); } + private IntermediatePacketStructData? TransformPacketStructs(GeneratorSyntaxContext syntaxContext, + CancellationToken cancellationToken) { + var structDeclaration = (StructDeclarationSyntax)syntaxContext.Node; + var model = syntaxContext.SemanticModel; + var symbol = + ModelExtensions.GetDeclaredSymbol(model, structDeclaration, cancellationToken: cancellationToken) as + INamedTypeSymbol; + var requiredInterfaces = new[] { "IPacket", "IIncomingPacket", "IOutgoingPacket", "IBidirectionalPacket" }; + var implementsInterface = symbol != null && + symbol.AllInterfaces.Any(i => + requiredInterfaces.Contains(i.Name, StringComparer.Ordinal)); + if (!implementsInterface) { + // TODO: https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.cookbook.md#issue-diagnostics + // or Analyzer + /*var diagnostic = Diagnostic.Create(_rpmGen001Diagnostic, symbol?.Locations.First(), + symbol?.ToDisplayString());*/ + } + + // Check for the marker attribute + var attribute = symbol?.GetAttributes() + .FirstOrDefault(attr => { + var attrClass = attr.AttributeClass; + while (attrClass != null) { + if (string.Equals(attrClass.Name, "PacketIdAttribute", StringComparison.Ordinal) && + attrClass.ContainingNamespace != null && + string.Equals(attrClass.ContainingNamespace.ToDisplayString(), "RaiNote.PacketMediator", + StringComparison.Ordinal)) { + return true; + } + + attrClass = attrClass.BaseType; + } + + return false; + }); + if (attribute == null) { + return null; + } + + var attributeConstructorArgument = attribute.ConstructorArguments[0]; + var enumType = attributeConstructorArgument.Type; + var enumValue = attributeConstructorArgument.Value; + + var enumMember = enumType?.GetMembers() + .OfType() + .FirstOrDefault(f => f.ConstantValue?.Equals(enumValue) == true); + + var enumMaxValue = enumType?.GetMembers() + .OfType() + .Max(x => x.ConstantValue); + + if (symbol == null || enumMember == null || enumMaxValue == null || enumType == null || + enumValue == null) return null; + + var intermediatePacketStructData = new IntermediatePacketStructData(symbol.Locations.First(), + symbol.ToDisplayString(), enumValue.ToString(), enumType.ToDisplayString(), enumMember.ToDisplayString(), + enumMaxValue.ToString(), implementsInterface); + + return intermediatePacketStructData; + } + private static IntermediatePacketHandlerData? TransformPacketHandlers(GeneratorSyntaxContext syntaxContext, CancellationToken cancellationToken) { var classDeclaration = (ClassDeclarationSyntax)syntaxContext.Node; @@ -286,8 +291,8 @@ public class PacketMediatorGenerator : IIncrementalGenerator { var symbol = ModelExtensions.GetDeclaredSymbol(model, classDeclaration, cancellationToken: cancellationToken) as INamedTypeSymbol; - var packetStruct = (symbol.Interfaces.Select(interfaceSyntax => { - if (!interfaceSyntax.Name.Equals("IPacketHandler")) { + var packetStruct = (symbol?.Interfaces.Select(interfaceSyntax => { + if (!interfaceSyntax.Name.Equals("IPacketHandler", StringComparison.Ordinal)) { return null; } @@ -313,10 +318,10 @@ public class PacketMediatorGenerator : IIncrementalGenerator { Expression: MemberAccessExpressionSyntax { Name: { } nameSyntax } } invocation && context.SemanticModel.GetOperation(context.Node, - cancellationToken) is IInvocationOperation targetOperation && - targetOperation.TargetMethod is - { Name: "AddPacketHandlerServices", ContainingNamespace: { Name: "RaiNote.PacketMediator" } } - ) { + cancellationToken) is IInvocationOperation { + TargetMethod: + { Name: "AddPacketHandlerServices", ContainingNamespace.Name: "RaiNote.PacketMediator" } + }) { #pragma warning disable RSEXPERIMENTAL002 // / Experimental interceptable location API if (context.SemanticModel.GetInterceptableLocation(invocation, cancellationToken: cancellationToken) is { } location) { @@ -338,6 +343,6 @@ public class PacketMediatorGenerator : IIncrementalGenerator { } #pragma warning disable RSEXPERIMENTAL002 // / Experimental interceptable location API - public record CandidateInvocation(InterceptableLocation Location); + private record CandidateInvocation(InterceptableLocation Location); #pragma warning restore RSEXPERIMENTAL002 } diff --git a/RaiNote.PacketMediator/RaiNote.PacketMediator.csproj b/RaiNote.PacketMediator/RaiNote.PacketMediator.csproj index d83398d..3c588ea 100644 --- a/RaiNote.PacketMediator/RaiNote.PacketMediator.csproj +++ b/RaiNote.PacketMediator/RaiNote.PacketMediator.csproj @@ -48,6 +48,14 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/RaiNote.PacketMediator/RaiNote.PacketMediator.csproj.DotSettings b/RaiNote.PacketMediator/RaiNote.PacketMediator.csproj.DotSettings new file mode 100644 index 0000000..89316e4 --- /dev/null +++ b/RaiNote.PacketMediator/RaiNote.PacketMediator.csproj.DotSettings @@ -0,0 +1,2 @@ + + Library \ No newline at end of file