Skip to content

Commit

Permalink
Support arrays of nullables for ILogged
Browse files Browse the repository at this point in the history
  • Loading branch information
ThadHouse committed Feb 18, 2024
1 parent a5d8a97 commit add8726
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 29 deletions.
12 changes: 12 additions & 0 deletions sourcegeneration/StereologueSourceGenerator/LogGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
static (spc, source) => source.ExecuteSourceGeneration(spc));
}
}

// Notes on what analyzer needs to block
// * Type marked [GenerateLog] is not partial
// * Type marked [GenerateLog] is interface (Might be fixed)
// * Array Like types of Nullable<T> for [Log] marked members
// * Pointer types for [Log] marked members
// * Only Allow Span, ROS and [] for array types
// * If array, only allow Long for integers
// * Types must be either primitives, ILogged, Array of ILogged, IStructSerializable, Array of IStructSerializable, or IProtobufSerializable

// What analyzer needs to warn
// * Class contains [Log] annotations, but is not marked [GenerateLog]
18 changes: 13 additions & 5 deletions sourcegeneration/StereologueSourceGenerator/LoggableMember.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,24 +91,32 @@ private static DeclarationKind GetInnerType(this ITypeSymbol typeSymbol, out ITy

token.ThrowIfCancellationRequested();

// TODO support IntPtr and NIntPtr

if (typeSymbol.SpecialType != SpecialType.None)
{
// We're a built in special type, no need to check for anything else
return new(DeclarationType.SpecialType, typeSymbol.SpecialType, nestedKind);
}

// See if we need to unwrap a nullable array
bool innerNullable = false;
if (typeSymbol.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
{
var namedTypeSymbol = (INamedTypeSymbol)typeSymbol;
typeSymbol = namedTypeSymbol.TypeArguments[0];
innerNullable = true;
}

// If we know we're generating a loggable implementation
if (typeSymbol.GetAttributes().Where(x => x.AttributeClass?.ToDisplayString() == "Stereologue.GenerateLogAttribute").Any())
{
return new(DeclarationType.Logged, SpecialType.None, nestedKind);

return new(DeclarationType.Logged, (innerNullable | typeSymbol.IsReferenceType) ? SpecialType.System_Nullable_T : SpecialType.None, nestedKind);
}
token.ThrowIfCancellationRequested();
// If we know we already implement ILogged
if (typeSymbol.AllInterfaces.Where(x => x.ToDisplayString() == "Stereologue.ILogged").Any())
{
return new(DeclarationType.Logged, SpecialType.None, nestedKind);
return new(DeclarationType.Logged, (innerNullable | typeSymbol.IsReferenceType) ? SpecialType.System_Nullable_T : SpecialType.None, nestedKind);
}
token.ThrowIfCancellationRequested();
// If we have an UpdateMonologue function
Expand All @@ -132,7 +140,7 @@ private static DeclarationKind GetInnerType(this ITypeSymbol typeSymbol, out ITy
}
if (parameters[0].Type.SpecialType == SpecialType.System_String && parameters[1].Type.ToDisplayString() == "Stereologue.Stereologuer")
{
return new(DeclarationType.Logged, SpecialType.None, nestedKind);
return new(DeclarationType.Logged, (innerNullable | typeSymbol.IsReferenceType) ? SpecialType.System_Nullable_T : SpecialType.None, nestedKind);
}
}
}
Expand Down
84 changes: 60 additions & 24 deletions sourcegeneration/StereologueSourceGenerator/LoggableType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,40 @@ private static void ConstructCall(LoggableMember data, StringBuilder builder, So
MemberType.Field => data.Name,
MemberType.Property => data.Name,
MemberType.Method => $"{data.Name}()",
_ => "Unknown member type"
_ => "ScrollUpInLogForActualError_WPILIB1001"
};

var path = string.IsNullOrWhiteSpace(data.AttributeInfo.Path) ? data.Name : data.AttributeInfo.Path;

if (data.MemberDeclaration.LoggedType == DeclarationType.Logged)
{
// If we're a basic logged, just do a simple ? based null check if possible.
if (data.MemberDeclaration.LoggedKind == DeclarationKind.None || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableReferenceType)
{
builder.Append(getOperation);
if (data.MemberDeclaration.LoggedKind != DeclarationKind.None)
{
builder.Append("?");
}
builder.Append(".UpdateStereologue($\"{path}/");
builder.Append(path);
builder.Append("\", logger);");
}
else
{
// We're an array, loop
builder.AppendLine($"foreach(var __tmpValue in {getOperation})");
builder.AppendLine(" {");
builder.Append(" __tmpValue");
if (data.MemberDeclaration.SpecialType == SpecialType.System_Nullable_T)
{
builder.Append("?");
}
builder.Append(".UpdateStereologue($\"{path}/");
builder.Append(path);
builder.AppendLine("\", logger);");
builder.AppendLine(" }");
}
return;
}

Expand All @@ -92,39 +119,23 @@ private static void ConstructCall(LoggableMember data, StringBuilder builder, So
{
logMethod = "LogStruct";
}
if (data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
{
getOperation = $"{getOperation}.GetValueOrDefault()";
}
}

else if (data.MemberDeclaration.LoggedType == DeclarationType.Protobuf)
{

if (data.MemberDeclaration.LoggedKind != DeclarationKind.None && data.MemberDeclaration.LoggedKind != DeclarationKind.NullableValueType && data.MemberDeclaration.LoggedKind != DeclarationKind.NullableReferenceType)
{
logMethod = "Cannot log an array of protobufs";
logMethod = "ScrollUpInLogForActualError_WPILIB1004";
}
else
{
logMethod = "LogProto";
}
if (data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
{
getOperation = $"{getOperation}.GetValueOrDefault()";
}
}
else if (data.MemberDeclaration.LoggedKind == DeclarationKind.None || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableReferenceType || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
{
// We're not an array. We're either Nullable<T> or a plain type
if (data.MemberDeclaration.SpecialType == SpecialType.System_String)
{
getOperation = $"{getOperation}.AsSpan()";
}
else if (data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
{
getOperation = $"{getOperation}.GetValueOrDefault()";
}
if (data.MemberDeclaration.SpecialType == SpecialType.System_UInt64 || data.MemberDeclaration.SpecialType == SpecialType.System_IntPtr || data.MemberDeclaration.SpecialType == SpecialType.System_UIntPtr)
{
getOperation = $"(long){getOperation}";
Expand All @@ -147,16 +158,12 @@ private static void ConstructCall(LoggableMember data, StringBuilder builder, So
SpecialType.System_UInt64 => "LogInteger",
SpecialType.System_IntPtr => "LogInteger",
SpecialType.System_UIntPtr => "LogInteger",
_ => $"Unknown Type: {data.MemberDeclaration}"
_ => $"ScrollUpInLogForActualError_WPILIB1002"
};
}
else
{
// We're array of a basic type
if (data.MemberDeclaration.LoggedKind != DeclarationKind.ReadOnlySpan && data.MemberDeclaration.LoggedKind != DeclarationKind.Span)
{
getOperation = $"{getOperation}.AsSpan()";
}

logMethod = data.MemberDeclaration.SpecialType switch
{
Expand All @@ -166,10 +173,32 @@ private static void ConstructCall(LoggableMember data, StringBuilder builder, So
SpecialType.System_Double => "LogDoubleArray",
SpecialType.System_Byte => "LogRaw",
SpecialType.System_Int64 => "LogIntegerArray",
_ => $"Unknown Array: {data.MemberDeclaration}"
_ => $"ScrollUpInLogForActualError_WPILIB1003"
};
}

bool isNullable = false;

if (data.MemberDeclaration.LoggedKind == DeclarationKind.Array || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableReferenceType || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
{
// We're nullable. We need to do some tricks.
builder.AppendLine("{");
builder.AppendLine($" var __tmpValue = {getOperation};");
builder.AppendLine($" if (__tmpValue is not null)");
builder.AppendLine(" {");
builder.Append(" ");
getOperation = "__tmpValue";
if (data.MemberDeclaration.SpecialType == SpecialType.System_String || data.MemberDeclaration.LoggedKind == DeclarationKind.Array)
{
getOperation = $"{getOperation}.AsSpan()";
}
if (data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
{
getOperation = $"{getOperation}.Value";
}
isNullable = true;
}

builder.Append("logger.");
builder.Append(logMethod);
builder.Append("($\"{path}/");
Expand All @@ -181,6 +210,13 @@ private static void ConstructCall(LoggableMember data, StringBuilder builder, So
builder.Append(", ");
builder.Append(data.AttributeInfo.LogLevel);
builder.Append(");");

if (isNullable)
{
builder.AppendLine();
builder.AppendLine(" }");
builder.Append(" }");
}
}

public static void AddClassDeclaration(LoggableType type, StringBuilder builder)
Expand Down
32 changes: 32 additions & 0 deletions test/stereologue.test/TestTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,32 @@ public partial class GenerateAllKnownTypes
[Log()]
public Rotation2d[] RotationArray = [];

// [Log()]
// public Rotation2d?[] RotationNullableArray = [];

// [Log(UseProtobuf = true)]
// public Rotation2d[] RotationProtoArray = [];

// [Log(UseProtobuf = true)]
// public Rotation2d?[] RotationProtoNullableArray = [];

// [Log]
// public object Object = null!;

// [Log]
// public unsafe int* PtrValue;

// [Log]
// public IEnumerable<bool> BoolEnumerable = null!;

// [Log]
// public int[] intArr = null!;

// [Log]
// public DateTime dateTime;

// public DateTime[] dateTimeArray = null!;

[Log]
public int? NullableInt;

Expand All @@ -189,4 +215,10 @@ public partial class GenerateAllKnownTypes
public GenerateClass NonNullClass = null!;
[Log]
public GenerateClass? NullClass;
[Log]
public GenerateClass[] ClassArray = null!;
[Log]
public GenerateClass?[] NullableClassArray = null!;
[Log]
public GenerateStruct?[] NullableStructArray = null!;
}

0 comments on commit add8726

Please sign in to comment.