Skip to content

Commit add8726

Browse files
committed
Support arrays of nullables for ILogged
1 parent a5d8a97 commit add8726

File tree

4 files changed

+117
-29
lines changed

4 files changed

+117
-29
lines changed

sourcegeneration/StereologueSourceGenerator/LogGenerator.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
1919
static (spc, source) => source.ExecuteSourceGeneration(spc));
2020
}
2121
}
22+
23+
// Notes on what analyzer needs to block
24+
// * Type marked [GenerateLog] is not partial
25+
// * Type marked [GenerateLog] is interface (Might be fixed)
26+
// * Array Like types of Nullable<T> for [Log] marked members
27+
// * Pointer types for [Log] marked members
28+
// * Only Allow Span, ROS and [] for array types
29+
// * If array, only allow Long for integers
30+
// * Types must be either primitives, ILogged, Array of ILogged, IStructSerializable, Array of IStructSerializable, or IProtobufSerializable
31+
32+
// What analyzer needs to warn
33+
// * Class contains [Log] annotations, but is not marked [GenerateLog]

sourcegeneration/StereologueSourceGenerator/LoggableMember.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,32 @@ private static DeclarationKind GetInnerType(this ITypeSymbol typeSymbol, out ITy
9191

9292
token.ThrowIfCancellationRequested();
9393

94-
// TODO support IntPtr and NIntPtr
95-
9694
if (typeSymbol.SpecialType != SpecialType.None)
9795
{
9896
// We're a built in special type, no need to check for anything else
9997
return new(DeclarationType.SpecialType, typeSymbol.SpecialType, nestedKind);
10098
}
10199

100+
// See if we need to unwrap a nullable array
101+
bool innerNullable = false;
102+
if (typeSymbol.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
103+
{
104+
var namedTypeSymbol = (INamedTypeSymbol)typeSymbol;
105+
typeSymbol = namedTypeSymbol.TypeArguments[0];
106+
innerNullable = true;
107+
}
108+
102109
// If we know we're generating a loggable implementation
103110
if (typeSymbol.GetAttributes().Where(x => x.AttributeClass?.ToDisplayString() == "Stereologue.GenerateLogAttribute").Any())
104111
{
105-
return new(DeclarationType.Logged, SpecialType.None, nestedKind);
112+
113+
return new(DeclarationType.Logged, (innerNullable | typeSymbol.IsReferenceType) ? SpecialType.System_Nullable_T : SpecialType.None, nestedKind);
106114
}
107115
token.ThrowIfCancellationRequested();
108116
// If we know we already implement ILogged
109117
if (typeSymbol.AllInterfaces.Where(x => x.ToDisplayString() == "Stereologue.ILogged").Any())
110118
{
111-
return new(DeclarationType.Logged, SpecialType.None, nestedKind);
119+
return new(DeclarationType.Logged, (innerNullable | typeSymbol.IsReferenceType) ? SpecialType.System_Nullable_T : SpecialType.None, nestedKind);
112120
}
113121
token.ThrowIfCancellationRequested();
114122
// If we have an UpdateMonologue function
@@ -132,7 +140,7 @@ private static DeclarationKind GetInnerType(this ITypeSymbol typeSymbol, out ITy
132140
}
133141
if (parameters[0].Type.SpecialType == SpecialType.System_String && parameters[1].Type.ToDisplayString() == "Stereologue.Stereologuer")
134142
{
135-
return new(DeclarationType.Logged, SpecialType.None, nestedKind);
143+
return new(DeclarationType.Logged, (innerNullable | typeSymbol.IsReferenceType) ? SpecialType.System_Nullable_T : SpecialType.None, nestedKind);
136144
}
137145
}
138146
}

sourcegeneration/StereologueSourceGenerator/LoggableType.cs

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,40 @@ private static void ConstructCall(LoggableMember data, StringBuilder builder, So
6969
MemberType.Field => data.Name,
7070
MemberType.Property => data.Name,
7171
MemberType.Method => $"{data.Name}()",
72-
_ => "Unknown member type"
72+
_ => "ScrollUpInLogForActualError_WPILIB1001"
7373
};
7474

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

7777
if (data.MemberDeclaration.LoggedType == DeclarationType.Logged)
7878
{
79+
// If we're a basic logged, just do a simple ? based null check if possible.
80+
if (data.MemberDeclaration.LoggedKind == DeclarationKind.None || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableReferenceType)
81+
{
82+
builder.Append(getOperation);
83+
if (data.MemberDeclaration.LoggedKind != DeclarationKind.None)
84+
{
85+
builder.Append("?");
86+
}
87+
builder.Append(".UpdateStereologue($\"{path}/");
88+
builder.Append(path);
89+
builder.Append("\", logger);");
90+
}
91+
else
92+
{
93+
// We're an array, loop
94+
builder.AppendLine($"foreach(var __tmpValue in {getOperation})");
95+
builder.AppendLine(" {");
96+
builder.Append(" __tmpValue");
97+
if (data.MemberDeclaration.SpecialType == SpecialType.System_Nullable_T)
98+
{
99+
builder.Append("?");
100+
}
101+
builder.Append(".UpdateStereologue($\"{path}/");
102+
builder.Append(path);
103+
builder.AppendLine("\", logger);");
104+
builder.AppendLine(" }");
105+
}
79106
return;
80107
}
81108

@@ -92,39 +119,23 @@ private static void ConstructCall(LoggableMember data, StringBuilder builder, So
92119
{
93120
logMethod = "LogStruct";
94121
}
95-
if (data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
96-
{
97-
getOperation = $"{getOperation}.GetValueOrDefault()";
98-
}
99122
}
100123

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

104127
if (data.MemberDeclaration.LoggedKind != DeclarationKind.None && data.MemberDeclaration.LoggedKind != DeclarationKind.NullableValueType && data.MemberDeclaration.LoggedKind != DeclarationKind.NullableReferenceType)
105128
{
106-
logMethod = "Cannot log an array of protobufs";
129+
logMethod = "ScrollUpInLogForActualError_WPILIB1004";
107130
}
108131
else
109132
{
110133
logMethod = "LogProto";
111134
}
112-
if (data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
113-
{
114-
getOperation = $"{getOperation}.GetValueOrDefault()";
115-
}
116135
}
117136
else if (data.MemberDeclaration.LoggedKind == DeclarationKind.None || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableReferenceType || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
118137
{
119138
// We're not an array. We're either Nullable<T> or a plain type
120-
if (data.MemberDeclaration.SpecialType == SpecialType.System_String)
121-
{
122-
getOperation = $"{getOperation}.AsSpan()";
123-
}
124-
else if (data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
125-
{
126-
getOperation = $"{getOperation}.GetValueOrDefault()";
127-
}
128139
if (data.MemberDeclaration.SpecialType == SpecialType.System_UInt64 || data.MemberDeclaration.SpecialType == SpecialType.System_IntPtr || data.MemberDeclaration.SpecialType == SpecialType.System_UIntPtr)
129140
{
130141
getOperation = $"(long){getOperation}";
@@ -147,16 +158,12 @@ private static void ConstructCall(LoggableMember data, StringBuilder builder, So
147158
SpecialType.System_UInt64 => "LogInteger",
148159
SpecialType.System_IntPtr => "LogInteger",
149160
SpecialType.System_UIntPtr => "LogInteger",
150-
_ => $"Unknown Type: {data.MemberDeclaration}"
161+
_ => $"ScrollUpInLogForActualError_WPILIB1002"
151162
};
152163
}
153164
else
154165
{
155166
// We're array of a basic type
156-
if (data.MemberDeclaration.LoggedKind != DeclarationKind.ReadOnlySpan && data.MemberDeclaration.LoggedKind != DeclarationKind.Span)
157-
{
158-
getOperation = $"{getOperation}.AsSpan()";
159-
}
160167

161168
logMethod = data.MemberDeclaration.SpecialType switch
162169
{
@@ -166,10 +173,32 @@ private static void ConstructCall(LoggableMember data, StringBuilder builder, So
166173
SpecialType.System_Double => "LogDoubleArray",
167174
SpecialType.System_Byte => "LogRaw",
168175
SpecialType.System_Int64 => "LogIntegerArray",
169-
_ => $"Unknown Array: {data.MemberDeclaration}"
176+
_ => $"ScrollUpInLogForActualError_WPILIB1003"
170177
};
171178
}
172179

180+
bool isNullable = false;
181+
182+
if (data.MemberDeclaration.LoggedKind == DeclarationKind.Array || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableReferenceType || data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
183+
{
184+
// We're nullable. We need to do some tricks.
185+
builder.AppendLine("{");
186+
builder.AppendLine($" var __tmpValue = {getOperation};");
187+
builder.AppendLine($" if (__tmpValue is not null)");
188+
builder.AppendLine(" {");
189+
builder.Append(" ");
190+
getOperation = "__tmpValue";
191+
if (data.MemberDeclaration.SpecialType == SpecialType.System_String || data.MemberDeclaration.LoggedKind == DeclarationKind.Array)
192+
{
193+
getOperation = $"{getOperation}.AsSpan()";
194+
}
195+
if (data.MemberDeclaration.LoggedKind == DeclarationKind.NullableValueType)
196+
{
197+
getOperation = $"{getOperation}.Value";
198+
}
199+
isNullable = true;
200+
}
201+
173202
builder.Append("logger.");
174203
builder.Append(logMethod);
175204
builder.Append("($\"{path}/");
@@ -181,6 +210,13 @@ private static void ConstructCall(LoggableMember data, StringBuilder builder, So
181210
builder.Append(", ");
182211
builder.Append(data.AttributeInfo.LogLevel);
183212
builder.Append(");");
213+
214+
if (isNullable)
215+
{
216+
builder.AppendLine();
217+
builder.AppendLine(" }");
218+
builder.Append(" }");
219+
}
184220
}
185221

186222
public static void AddClassDeclaration(LoggableType type, StringBuilder builder)

test/stereologue.test/TestTree.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,32 @@ public partial class GenerateAllKnownTypes
177177
[Log()]
178178
public Rotation2d[] RotationArray = [];
179179

180+
// [Log()]
181+
// public Rotation2d?[] RotationNullableArray = [];
182+
183+
// [Log(UseProtobuf = true)]
184+
// public Rotation2d[] RotationProtoArray = [];
185+
186+
// [Log(UseProtobuf = true)]
187+
// public Rotation2d?[] RotationProtoNullableArray = [];
188+
189+
// [Log]
190+
// public object Object = null!;
191+
192+
// [Log]
193+
// public unsafe int* PtrValue;
194+
195+
// [Log]
196+
// public IEnumerable<bool> BoolEnumerable = null!;
197+
198+
// [Log]
199+
// public int[] intArr = null!;
200+
201+
// [Log]
202+
// public DateTime dateTime;
203+
204+
// public DateTime[] dateTimeArray = null!;
205+
180206
[Log]
181207
public int? NullableInt;
182208

@@ -189,4 +215,10 @@ public partial class GenerateAllKnownTypes
189215
public GenerateClass NonNullClass = null!;
190216
[Log]
191217
public GenerateClass? NullClass;
218+
[Log]
219+
public GenerateClass[] ClassArray = null!;
220+
[Log]
221+
public GenerateClass?[] NullableClassArray = null!;
222+
[Log]
223+
public GenerateStruct?[] NullableStructArray = null!;
192224
}

0 commit comments

Comments
 (0)