Skip to content

Commit b8fe9c5

Browse files
committed
Add automatic status checker
1 parent c1de60e commit b8fe9c5

File tree

13 files changed

+560
-8
lines changed

13 files changed

+560
-8
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System.Text;
2+
3+
namespace WPILib.CodeHelpers;
4+
5+
public class IndentedStringBuilder
6+
{
7+
private readonly StringBuilder m_builder = new();
8+
private int m_scopeCount = 0;
9+
10+
public void StartLine()
11+
{
12+
m_builder.Append(' ', m_scopeCount * 4);
13+
}
14+
15+
public void Append(string value)
16+
{
17+
m_builder.Append(value);
18+
}
19+
20+
public void EndLine()
21+
{
22+
m_builder.AppendLine();
23+
}
24+
25+
public void AppendFullLine(string line)
26+
{
27+
StartLine();
28+
Append(line);
29+
EndLine();
30+
}
31+
32+
public IndentedScope EnterScope()
33+
{
34+
return new(this);
35+
}
36+
37+
public void EnterManualScope()
38+
{
39+
AppendFullLine("{");
40+
m_scopeCount++;
41+
}
42+
43+
public void ExitManualScope()
44+
{
45+
m_scopeCount--;
46+
AppendFullLine("}");
47+
}
48+
49+
public override string ToString()
50+
{
51+
return m_builder.ToString();
52+
}
53+
54+
public readonly ref struct IndentedScope
55+
{
56+
private readonly IndentedStringBuilder m_builder;
57+
58+
public IndentedScope(IndentedStringBuilder builder)
59+
{
60+
m_builder = builder;
61+
builder.AppendFullLine("{");
62+
m_builder.m_scopeCount++;
63+
}
64+
65+
public void Dispose()
66+
{
67+
m_builder.m_scopeCount--;
68+
m_builder.AppendFullLine("}");
69+
}
70+
}
71+
}
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
using System.Collections.Immutable;
2+
using System.Text;
3+
using Microsoft.CodeAnalysis;
4+
using WPILib.CodeHelpers.LogGenerator;
5+
6+
namespace WPILib.CodeHelpers.StatusCheckGenerator;
7+
8+
internal enum ReturnKind
9+
{
10+
None,
11+
Value,
12+
Ref
13+
}
14+
15+
internal record MethodModel(TypeDeclType TypeDeclType, string TypeName, string? TypeNamespace, string MethodDeclaration, string NameForCall, string TypeConstraints, string ReturnType, ReturnKind ReturnKind, bool NeedsUnsafe, EquatableArray<ParameterModel> Parameters)
16+
{
17+
public void AddClassDeclaration(IndentedStringBuilder builder)
18+
{
19+
builder.StartLine();
20+
if (TypeDeclType.IsReadOnly)
21+
{
22+
builder.Append("readonly ");
23+
}
24+
25+
if (TypeDeclType.IsRefLikeType)
26+
{
27+
builder.Append("ref ");
28+
}
29+
30+
if (NeedsUnsafe) {
31+
builder.Append("unsafe ");
32+
}
33+
34+
builder.Append("partial ");
35+
36+
if (TypeDeclType.IsRecord)
37+
{
38+
builder.Append("record ");
39+
}
40+
41+
if (TypeDeclType.TypeKind == TypeKind.Class)
42+
{
43+
builder.Append("class ");
44+
}
45+
else if (TypeDeclType.TypeKind == TypeKind.Struct)
46+
{
47+
builder.Append("struct ");
48+
}
49+
else if (TypeDeclType.TypeKind == TypeKind.Interface)
50+
{
51+
builder.Append("interface ");
52+
}
53+
54+
builder.Append(TypeName);
55+
builder.EndLine();
56+
}
57+
58+
public void WriteMethod(IndentedStringBuilder builder)
59+
{
60+
if (TypeNamespace is not null)
61+
{
62+
builder.AppendFullLine($"namespace {TypeNamespace}");
63+
builder.EnterManualScope();
64+
}
65+
66+
{
67+
AddClassDeclaration(builder);
68+
using var classScope = builder.EnterScope();
69+
WriteMethodDeclaration(builder);
70+
using var methodScope = builder.EnterScope();
71+
WriteCallString(builder);
72+
WriteStatusCheck(builder);
73+
WriteReturn(builder);
74+
}
75+
if (TypeNamespace is not null)
76+
{
77+
builder.ExitManualScope();
78+
}
79+
}
80+
81+
public void WriteMethodDeclaration(IndentedStringBuilder builder)
82+
{
83+
builder.StartLine();
84+
builder.Append(MethodDeclaration);
85+
builder.Append("(");
86+
bool first = true;
87+
foreach (var parameter in Parameters)
88+
{
89+
if (first)
90+
{
91+
first = false;
92+
}
93+
else
94+
{
95+
builder.Append(", ");
96+
}
97+
builder.Append(parameter.ParameterString);
98+
}
99+
builder.Append(")");
100+
builder.Append(TypeConstraints);
101+
builder.EndLine();
102+
}
103+
104+
public void WriteCallString(IndentedStringBuilder builder)
105+
{
106+
builder.StartLine();
107+
if (ReturnKind != ReturnKind.None)
108+
{
109+
builder.Append(ReturnType);
110+
builder.Append(" __tmpValue = ");
111+
if (ReturnKind == ReturnKind.Ref)
112+
{
113+
builder.Append("ref ");
114+
}
115+
}
116+
builder.Append(NameForCall);
117+
builder.Append("(");
118+
bool first = true;
119+
foreach (var parameter in Parameters)
120+
{
121+
if (first)
122+
{
123+
first = false;
124+
}
125+
else
126+
{
127+
builder.Append(", ");
128+
}
129+
parameter.WriteCallString(builder);
130+
}
131+
if (!first)
132+
{
133+
builder.Append(", ");
134+
}
135+
builder.Append("out var __tmpStatus);");
136+
builder.EndLine();
137+
}
138+
139+
public void WriteStatusCheck(IndentedStringBuilder builder)
140+
{
141+
builder.AppendFullLine("__tmpStatus.ThrowIfFailed();");
142+
}
143+
144+
public void WriteReturn(IndentedStringBuilder builder)
145+
{
146+
if (ReturnKind == ReturnKind.None)
147+
{
148+
return;
149+
}
150+
builder.StartLine();
151+
builder.Append("return ");
152+
if (ReturnKind == ReturnKind.Ref)
153+
{
154+
builder.Append("ref ");
155+
}
156+
builder.Append("__tmpValue;");
157+
builder.EndLine();
158+
}
159+
}
160+
161+
internal static class MethodModelExtensions
162+
{
163+
public static MethodModel? GetMethodModel(this IMethodSymbol symbol)
164+
{
165+
var parameters = ImmutableArray.CreateBuilder<ParameterModel>(symbol.Parameters.Length);
166+
167+
if (symbol.Parameters.IsEmpty)
168+
{
169+
return null;
170+
}
171+
172+
if (symbol.Parameters[^1].RefKind != RefKind.Out)
173+
{
174+
return null;
175+
}
176+
177+
var symbolParameters = symbol.Parameters;
178+
bool needsUnsafe = false;
179+
180+
for (int i = 0; i < symbolParameters.Length - 1; i++)
181+
{
182+
parameters.Add(symbolParameters[i].GetParameterModel(ref needsUnsafe));
183+
}
184+
185+
var methodDeclarationFormat = new SymbolDisplayFormat(
186+
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
187+
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
188+
memberOptions: SymbolDisplayMemberOptions.IncludeType | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeRef
189+
190+
);
191+
192+
var methodDeclaration = symbol.ToDisplayString(methodDeclarationFormat);
193+
194+
var callDeclarationFormat = new SymbolDisplayFormat(
195+
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
196+
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
197+
memberOptions: SymbolDisplayMemberOptions.IncludeRef
198+
);
199+
200+
var callDeclaration = symbol.ToDisplayString(callDeclarationFormat);
201+
202+
var constraintsFmt = new SymbolDisplayFormat(
203+
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeConstraints
204+
);
205+
206+
var constraints = symbol.ToDisplayString(constraintsFmt).Replace(symbol.Name, "");
207+
208+
var returnTypeFmt = new SymbolDisplayFormat(
209+
memberOptions: SymbolDisplayMemberOptions.IncludeType | SymbolDisplayMemberOptions.IncludeRef,
210+
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces
211+
);
212+
213+
var returnType = symbol.ToDisplayString(returnTypeFmt).Replace(symbol.Name, "");
214+
215+
if (symbol.ReturnType.RequiresUnsafe()) {
216+
needsUnsafe = true;
217+
}
218+
219+
ReturnKind retKind;
220+
221+
if (symbol.RefKind == RefKind.RefReadOnly || symbol.RefKind == RefKind.Ref)
222+
{
223+
retKind = ReturnKind.Ref;
224+
}
225+
else if (symbol.ReturnsVoid)
226+
{
227+
retKind = ReturnKind.None;
228+
returnType = "void";
229+
}
230+
else
231+
{
232+
retKind = ReturnKind.Value;
233+
}
234+
235+
var classSymbol = symbol.ContainingType;
236+
237+
var nameString = classSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
238+
var nspace = classSymbol.ContainingNamespace is { IsGlobalNamespace: false } ns ? ns.ToDisplayString() : null;
239+
240+
return new(classSymbol.GetTypeDeclType(), nameString, nspace, methodDeclaration, callDeclaration, constraints, returnType, retKind, needsUnsafe, parameters.ToImmutable());
241+
}
242+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.Text;
2+
using Microsoft.CodeAnalysis;
3+
4+
namespace WPILib.CodeHelpers.StatusCheckGenerator;
5+
6+
public record ParameterModel(string ParameterString, string Name, RefKind RefKind)
7+
{
8+
public void WriteCallString(IndentedStringBuilder builder)
9+
{
10+
if (RefKind == RefKind.RefReadOnlyParameter) {
11+
builder.Append("in ");
12+
} else if (RefKind == RefKind.Ref) {
13+
builder.Append("ref ");
14+
} else if (RefKind == RefKind.Out) {
15+
builder.Append("out ");
16+
}
17+
builder.Append(Name);
18+
}
19+
}
20+
21+
internal static class ParameterModelExtensions
22+
{
23+
public static ParameterModel GetParameterModel(this IParameterSymbol symbol, ref bool needsUnsafe)
24+
{
25+
var fmt = new SymbolDisplayFormat(
26+
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
27+
parameterOptions: SymbolDisplayParameterOptions.IncludeExtensionThis |
28+
SymbolDisplayParameterOptions.IncludeModifiers |
29+
SymbolDisplayParameterOptions.IncludeType |
30+
SymbolDisplayParameterOptions.IncludeName |
31+
SymbolDisplayParameterOptions.IncludeDefaultValue,
32+
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
33+
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes
34+
);
35+
36+
if (symbol.Type.RequiresUnsafe()) {
37+
needsUnsafe = true;
38+
}
39+
40+
return new(symbol.ToDisplayString(fmt), symbol.Name, symbol.RefKind);
41+
}
42+
}

0 commit comments

Comments
 (0)