Skip to content

Commit

Permalink
Handle ref struct
Browse files Browse the repository at this point in the history
  • Loading branch information
ThadHouse committed Feb 17, 2024
1 parent d020a48 commit e48b1db
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ public class Ids
Ids.LoggableTypeNotSupported, "", "", Category, DiagnosticSeverity.Error, isEnabledByDefault: true, "");

public static readonly DiagnosticDescriptor GeneratedTypeIsInterface = new(
Ids.LoggableTypeNotSupported, "", "", Category, DiagnosticSeverity.Error, isEnabledByDefault: true, "");
Ids.LoggableTypeNotSupported, "Generated types cannot be ref", "", Category, DiagnosticSeverity.Error, isEnabledByDefault: true, "");

public static readonly DiagnosticDescriptor GeneratedTypeIsRefStruct = new(
Ids.GeneratedTypeIsRefStruct, "", "", Category, DiagnosticSeverity.Error, isEnabledByDefault: true, "");
Ids.GeneratedTypeIsRefStruct, "Generated types cannot be ref struct", "HelloWorld", Category, DiagnosticSeverity.Error, isEnabledByDefault: true, "");

public static readonly DiagnosticDescriptor LoggedMethodDoesntReturnVoid = new(
Ids.LoggedMethodDoesntReturnVoid, "", "", Category, DiagnosticSeverity.Error, isEnabledByDefault: true, "");
Expand All @@ -47,4 +47,4 @@ public class Ids
Ids.LoggedMethodTakesArguments, "", "", Category, DiagnosticSeverity.Error, isEnabledByDefault: true, "");
public static readonly DiagnosticDescriptor LoggedMemberTypeNotSupported = new(
Ids.LoggedMemberTypeNotSupported, "", "", Category, DiagnosticSeverity.Error, isEnabledByDefault: true, "");
}
}
50 changes: 32 additions & 18 deletions sourcegeneration/StereologueSourceGenerator/LogGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ internal record LogAttributeInfo(string Path, string LogLevel, string LogType, b

internal record LogData(string GetOperation, string? Type, DeclarationType DecelType, LogAttributeInfo AttributeInfo);

internal record ClassData(EquatableArray<LogData> LoggedItems, string Name, string ClassDeclaration, string? Namespace);
internal record ClassData(EquatableArray<LogData> LoggedItems, string Name, string ClassDeclaration, bool IsRefStruct, string? Namespace);

internal record ClassOrDiagnostic(ClassData? ValidClassData, EquatableArray<DiagnosticInfo> Diagnostic);

Expand Down Expand Up @@ -147,21 +147,47 @@ public class LogGenerator : IIncrementalGenerator
}

var fmt = new SymbolDisplayFormat(genericsOptions: SymbolDisplayGenericsOptions.None);
var fileName = $"{classSymbol.ContainingNamespace}{classSymbol.ToDisplayString(fmt)}{classSymbol.MetadataName}";

return new ClassOrDiagnostic(new ClassData(loggableMembers.ToImmutable(), $"{classSymbol.ContainingNamespace}{classSymbol.ToDisplayString(fmt)}{classSymbol.MetadataName}", typeBuilder.ToString(), ns), diagnosticList.ToImmutable());
return new ClassOrDiagnostic(new ClassData(loggableMembers.ToImmutable(), $"{classSymbol.ContainingNamespace}{classSymbol.ToDisplayString(fmt)}{classSymbol.MetadataName}", typeBuilder.ToString(), classSymbol.IsRefLikeType, ns), diagnosticList.ToImmutable());
}

private static LogData ComputeOperation(ITypeSymbol logType, string getOp, LogAttributeInfo attributeInfo)
{
// If we know we're generating a loggable implementation
if (logType.GetAttributes().Where(x => x.AttributeClass?.ToDisplayString() == "Stereologue.GenerateLogAttribute").Any())
{
return new LogData(getOp, null, DeclarationType.Logged, attributeInfo);
}
// If we know we already implement ILogged
if (logType.AllInterfaces.Where(x => x.ToDisplayString() == "Stereologue.ILogged").Any())
{
return new LogData(getOp, null, DeclarationType.Logged, attributeInfo);
}

// If we have an UpdateMonologue function
var members = logType.GetMembers("UpdateStereologue");
foreach (var member in members)
{
// Must be a method
if (member is IMethodSymbol method)
{
// Must return void
if (!method.ReturnsVoid)
{
continue;
}
// Must have a string first parameter, and a Stereologue.Stereologuer second paramter
var parameters = method.Parameters;
if (parameters.Length != 2)
{
continue;
}
if (parameters[0].Type.SpecialType == SpecialType.System_String && parameters[1].Type.ToDisplayString() == "Stereologue.Stereologuer")
{
return new LogData(getOp, null, DeclarationType.Logged, attributeInfo);
}
}
}

var fmt = new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters);
var fullTypeName = logType.ToDisplayString(fmt);
var structName = $"WPIUtil.Serialization.Struct.IStructSerializable<{fullTypeName}>";
Expand Down Expand Up @@ -209,7 +235,7 @@ static void ConstructCall(LogData data, StringBuilder builder, SourceProductionC
switch (data.DecelType)
{
case DeclarationType.Logged:
builder.AppendLine($"{data.GetOperation}?.UpdateStereologue($\"{{path}}/{data.AttributeInfo.Path}\", logger, {data.AttributeInfo.LogLevel});");
builder.AppendLine($"{data.GetOperation}.UpdateStereologue($\"{{path}}/{data.AttributeInfo.Path}\", logger, {data.AttributeInfo.LogLevel});");
return;
case DeclarationType.Struct:
builder.AppendLine($"logger.LogStruct($\"{{path}}/{data.AttributeInfo.Path}\", {data.AttributeInfo.LogType}, {data.GetOperation}, {data.AttributeInfo.LogLevel});");
Expand Down Expand Up @@ -284,7 +310,7 @@ static void Execute(ClassOrDiagnostic? classData, SourceProductionContext contex
builder.AppendLine($"namespace {value.Namespace};");
builder.AppendLine();

builder.AppendLine(value.ClassDeclaration);
builder.AppendLine($"{value.ClassDeclaration}{(value.IsRefStruct ? "" : " : Stereologue.ILogged")}");
builder.AppendLine("{");
builder.AppendLine(" public void UpdateStereologue(string path, Stereologue.Stereologuer logger)");
builder.AppendLine(" {");
Expand All @@ -307,18 +333,6 @@ static void Execute(ClassOrDiagnostic? classData, SourceProductionContext contex
return DiagnosticInfo.Create(GeneratorDiagnostics.GeneratedTypeNotPartial, syntax.Identifier.GetLocation(), [symbol.Name, nonPartialIdentifier]);
}

// Ensure class doesn't implement ILogged
if (symbol.AllInterfaces.Where(x => x.ToDisplayString() == "Stereologue.ILogged").Any())
{
return DiagnosticInfo.Create(GeneratorDiagnostics.GeneratedTypeImplementsILogged, syntax.Identifier.GetLocation(), [symbol.Name]);
}

// Ensure implementation isn't ref struct
if (symbol.IsRefLikeType)
{
return DiagnosticInfo.Create(GeneratorDiagnostics.GeneratedTypeIsRefStruct, syntax.Identifier.GetLocation(), [symbol.Name]);
}

// Ensure implementation isn't interface
if (symbol.TypeKind == TypeKind.Interface)
{
Expand Down
26 changes: 13 additions & 13 deletions test/stereologue.test/TestTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@ public readonly partial struct GenerateReadonlyStruct
public int Variable { get; }
}

// [GenerateLog]
// public ref partial struct GenerateRefStruct
// {
// [Log]
// public int Variable { get; }
// }
[GenerateLog]
public ref partial struct GenerateRefStruct
{
[Log]
public int Variable { get; }
}

// [GenerateLog]
// public readonly ref partial struct GenerateReadonlyRefStruct
// {
// [Log]
// public int Variable { get; }
// }
[GenerateLog]
public readonly ref partial struct GenerateReadonlyRefStruct
{
[Log]
public int Variable { get; }
}

// [GenerateLog]
// public partial interface GenerateInterface
Expand All @@ -62,7 +62,7 @@ public readonly partial struct GenerateReadonlyStruct
// }

[GenerateLog]
public partial record GenerateRecord
public partial record GenerateRecord : ILogged
{
[Log]
public int Variable { get; }
Expand Down

0 comments on commit e48b1db

Please sign in to comment.