Skip to content

Commit 2147d61

Browse files
SourceGen, Analysis: Support some generic collections, arrays, and enums.
1 parent d6f1105 commit 2147d61

File tree

5 files changed

+188
-43
lines changed

5 files changed

+188
-43
lines changed

EppNet-SourceGen/Source/Analysis/NetworkObjectAnalysis.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/////////////////////////////////////////////
2-
/// Filename: ResolverAnalysis.cs
2+
/// Filename: NetworkObjectAnalysis.cs
33
/// Date: February 9, 2025
44
/// Authors: Maverick Liberty
55
//////////////////////////////////////////////

EppNet-SourceGen/Source/Globals.cs

Lines changed: 113 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ public static class Globals
5151
public const string NetMethodAttr = "NetworkMethod";
5252
public const string NetMethodAttrFullName = AttrPath + NetMethodAttr + Attribute;
5353

54+
public static readonly string[] SupportedTypes =
55+
[
56+
"System.Collections.Generic.List",
57+
"System.Collections.Generic.HashSet",
58+
"System.Collections.Generic.SortedSet",
59+
"System.Collections.Generic.Dictionary",
60+
"System.Collections.Generic.LinkedList"
61+
];
62+
5463
//////////////////////////////////////////////
5564
/// Descriptors
5665
//////////////////////////////////////////////
@@ -131,6 +140,102 @@ public static bool HasAttribute(MethodDeclarationSyntax methodNode, string attrN
131140

132141
return false;
133142
}
143+
144+
145+
// Checks if the specified type name is valid
146+
// (IsValid, IsNetObject)
147+
public static (bool, bool) IsValidTypeName(string typeName, IDictionary<string, string> resolverDict,
148+
IDictionary<string, NetworkObjectModel?> objDict)
149+
{
150+
151+
if (SupportedTypes.Contains(typeName))
152+
return (true, false);
153+
154+
if (resolverDict.ContainsKey(typeName))
155+
return (true, false);
156+
157+
if (objDict.ContainsKey(typeName))
158+
return (true, true);
159+
160+
return (false, false);
161+
}
162+
163+
public static NetworkParameterTypeModel? ExamineType(TypeSyntax type, SemanticModel semModel,
164+
IDictionary<string, string> resolverDict,
165+
IDictionary<string, NetworkObjectModel?> objDict, CancellationToken cancelToken = default)
166+
{
167+
// We want to:
168+
// 1) Examine the type to see if it's supported.
169+
// - 1a: If it's a generic name, ensure the base type name is valid.
170+
// - Enums, Dictionary, List, HashSet, SortedSet, and LinkedList are valid
171+
// 2) Check if the type has a resolver.
172+
NetworkParameterTypeModel? model = null;
173+
ITypeSymbol typeSymbol = semModel.GetTypeInfo(type, cancelToken).Type;
174+
175+
cancelToken.ThrowIfCancellationRequested();
176+
177+
if (typeSymbol == null)
178+
return model;
179+
180+
if (typeSymbol.TypeKind == TypeKind.Enum)
181+
{
182+
// This is an enum type. These always have an integral underlying type
183+
ITypeSymbol underlyingType = ((INamedTypeSymbol)typeSymbol).EnumUnderlyingType;
184+
string underlyingTypeName = underlyingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
185+
model = new NetworkParameterTypeModel(typeSymbol, null, underlyingType: underlyingTypeName);
186+
return model;
187+
}
188+
189+
if (type is ArrayTypeSyntax arrayType)
190+
return ExamineType(arrayType.ElementType, semModel, resolverDict, objDict, cancelToken);
191+
192+
if (type is GenericNameSyntax genericName)
193+
{
194+
string baseTypeName = $"{typeSymbol.ContainingNamespace.ToDisplayString()}.{genericName.Identifier.Text}";
195+
(bool isValidType, bool isNetObj) = IsValidTypeName(baseTypeName, resolverDict, objDict);
196+
197+
EquatableList<NetworkParameterTypeModel> subtypes = new();
198+
foreach (TypeSyntax typeArg in genericName.TypeArgumentList.Arguments)
199+
{
200+
var result = ExamineType(typeArg, semModel, resolverDict, objDict, cancelToken);
201+
202+
// Let's ensure the result is valid
203+
if (result == null)
204+
return null;
205+
206+
subtypes.Add(result.Value);
207+
}
208+
209+
model = new NetworkParameterTypeModel(typeSymbol, subtypes, null, isNetObject: isNetObj);
210+
}
211+
else if (type is TupleTypeSyntax tuple)
212+
{
213+
EquatableList<NetworkParameterTypeModel> subtypes = new();
214+
foreach (TupleElementSyntax tupleArg in tuple.Elements)
215+
{
216+
TypeSyntax typeArg = tupleArg.Type;
217+
var result = ExamineType(typeArg, semModel, resolverDict, objDict, cancelToken);
218+
219+
// Let's ensure the result is valid
220+
if (result == null)
221+
return null;
222+
223+
subtypes.Add(result.Value);
224+
}
225+
226+
model = new NetworkParameterTypeModel(typeSymbol, subtypes, null, isNetObject: false);
227+
}
228+
else
229+
{
230+
string fullTypeName = $"{typeSymbol.ContainingNamespace.ToDisplayString()}.{typeSymbol.Name}";
231+
(bool isValidType, bool isNetObj) = IsValidTypeName(fullTypeName, resolverDict, objDict);
232+
233+
if (isValidType)
234+
model = new NetworkParameterTypeModel(typeSymbol, null, isNetObject: isNetObj);
235+
}
236+
237+
return model;
238+
}
134239

135240
public static (NetworkMethodModel?, NetworkMethodAnalysisError, ParameterSyntax, TypeSyntax) LocateNetMethod(SyntaxNode node, SemanticModel semModel,
136241
IDictionary<string, string> resolverDict,
@@ -168,50 +273,24 @@ public static (NetworkMethodModel?, NetworkMethodAnalysisError, ParameterSyntax,
168273
// Step 3: Ensure the parameters types have a network resolver
169274
List<string> typeNames = new();
170275

276+
EquatableList<NetworkParameterTypeModel> parameters = new();
277+
171278
foreach (ParameterSyntax paramNode in methodNode.ParameterList.Parameters)
172279
{
173280
TypeSyntax type = paramNode.Type;
174-
ITypeSymbol typeSymbol = semModel.GetTypeInfo(type).Type;
175-
176-
if (typeSymbol == null)
177-
continue;
178281

179-
if (type is GenericNameSyntax genericName)
180-
{
181-
// Fully qualified base type name
182-
string baseTypeName = $"{typeSymbol.ContainingNamespace.ToDisplayString()}.{genericName.Identifier.Text}";
183-
//typeNames.Add(baseTypeName);
184-
185-
// Get generic argument type names
186-
foreach (var typeArg in genericName.TypeArgumentList.Arguments)
187-
{
188-
var typeArgSymbol = semModel.GetTypeInfo(typeArg).Type;
189-
if (typeArgSymbol != null)
190-
{
191-
string argTypeName = $"{typeArgSymbol.ContainingNamespace.ToDisplayString()}.{typeArgSymbol.Name}";
192-
typeNames.Add(argTypeName);
193-
}
194-
else
195-
{
196-
typeNames.Add(typeArg.ToString()); // Fallback to raw syntax name
197-
}
198-
}
199-
}
200-
else
201-
{
202-
string fullTypeName = $"{typeSymbol.ContainingNamespace.ToDisplayString()}.{typeSymbol.Name}";
203-
typeNames.Add(fullTypeName);
204-
}
282+
NetworkParameterTypeModel? typeModel = ExamineType(type, semModel, resolverDict, objDict, cancelToken);
205283

206-
foreach (string name in typeNames)
284+
if (!typeModel.HasValue)
207285
{
208-
if (!resolverDict.ContainsKey(name))
209-
return (model, error | NetworkMethodAnalysisError.MissingResolver, paramNode, type);
286+
// Type is invalid.
287+
return (model, error | NetworkMethodAnalysisError.MissingResolver, paramNode, type);
210288
}
211289

290+
parameters.Add(typeModel.Value);
212291
}
213292

214-
model = new NetworkMethodModel(symbol, typeNames.ToArray());
293+
model = new NetworkMethodModel(symbol, parameters);
215294

216295
INamedTypeSymbol classSymbol = semModel.GetDeclaredSymbol(classNode);
217296
string className = $"{classSymbol.ContainingNamespace.ToDisplayString()}.{classSymbol.Name}";

EppNet-SourceGen/Source/Models/NetworkMethodModel.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public enum NetworkMethodAnalysisError
3737
MissingResolver = 1 << 4
3838
}
3939

40-
public readonly struct NetworkMethodModel(ISymbol symbol, string[] parameterTypes) : IEquatable<NetworkMethodModel>
40+
public readonly struct NetworkMethodModel(ISymbol symbol, EquatableList<NetworkParameterTypeModel> parameters) : IEquatable<NetworkMethodModel>
4141
{
4242

4343
public string Name { get; } = symbol.Name;
@@ -53,7 +53,7 @@ public string FullNamespace
5353
}
5454
}
5555

56-
public string[] ParameterTypes { get; } = parameterTypes;
56+
public EquatableList<NetworkParameterTypeModel> Parameters { get; } = parameters;
5757

5858
public override bool Equals(object obj)
5959
=> obj is NetworkMethodModel model &&
@@ -62,13 +62,15 @@ public override bool Equals(object obj)
6262
public bool Equals(NetworkMethodModel other) =>
6363
Name == other.Name &&
6464
Namespace == other.Namespace &&
65-
ParameterTypes.Equals(other.ParameterTypes);
65+
Parameters == other.Parameters;
6666

6767
public override string ToString()
6868
{
6969
StringBuilder builder = new($"{Name}(");
7070

71-
for (int i = 0; i < ParameterTypes.Length; i++)
71+
/*foreach ()
72+
73+
for (int i = 0; i < Parameters.Length; i++)
7274
{
7375
string type = ParameterTypes[i];
7476
builder.Append(type);
@@ -77,14 +79,14 @@ public override string ToString()
7779
builder.Append(", ");
7880
}
7981
80-
builder.Append(")");
82+
builder.Append(")");*/
8183
return builder.ToString();
8284
}
8385

8486
public override int GetHashCode() =>
8587
Name.GetHashCode() ^
8688
Namespace.GetHashCode() ^
87-
ParameterTypes.GetHashCode();
89+
Parameters.GetHashCode();
8890

8991
public static bool operator ==(NetworkMethodModel left, NetworkMethodModel right) =>
9092
left.Equals(right);

EppNet-SourceGen/Source/Models/NetworkObjectModel.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,13 @@ public bool Equals(NetworkObjectModel other) =>
6565
Methods == other.Methods;
6666

6767
public override string ToString() =>
68-
$"{Name}";
68+
$"{FullyQualifiedName} Methods: {Methods.Count}";
6969

70-
public override int GetHashCode() => Name.GetHashCode() ^ Namespace.GetHashCode();
70+
public override int GetHashCode() =>
71+
Name.GetHashCode() ^
72+
Namespace.GetHashCode() ^
73+
FullyQualifiedName.GetHashCode() ^
74+
Methods.GetHashCode();
7175

7276
public static bool operator ==(NetworkObjectModel left, NetworkObjectModel right) => left.Equals(right);
7377
public static bool operator !=(NetworkObjectModel left, NetworkObjectModel right) => !left.Equals(right);
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/////////////////////////////////////////////
2+
/// Filename: NetworkParameterTypeModel.cs
3+
/// Date: February 18, 2025
4+
/// Authors: Maverick Liberty
5+
//////////////////////////////////////////////
6+
7+
using Microsoft.CodeAnalysis;
8+
9+
using System;
10+
11+
namespace EppNet.SourceGen.Models
12+
{
13+
14+
public readonly struct NetworkParameterTypeModel(ISymbol symbol, EquatableList<NetworkParameterTypeModel> subtypes, string underlyingType = null, bool isNetObject = false)
15+
: IEquatable<NetworkParameterTypeModel>
16+
{
17+
18+
public string Name { get; } = symbol.Name;
19+
public string Namespace { get; } = symbol.ContainingNamespace.Name;
20+
public string FullyQualifiedName { get; } = $"{symbol.ContainingNamespace.ToDisplayString()}.{symbol.Name}";
21+
public string UnderlyingTypeFullyQualifiedName { get; } = underlyingType;
22+
23+
public string TypeAsWritten { get; } = symbol.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat);
24+
25+
public EquatableList<NetworkParameterTypeModel> Subtypes { get; } = subtypes;
26+
27+
public bool IsNetObject { get; } = isNetObject;
28+
29+
public override bool Equals(object obj) =>
30+
obj is NetworkParameterTypeModel model &&
31+
Equals(model);
32+
33+
public bool Equals(NetworkParameterTypeModel other) =>
34+
Name == other.Name &&
35+
Namespace == other.Namespace &&
36+
FullyQualifiedName == other.FullyQualifiedName &&
37+
Subtypes == other.Subtypes &&
38+
UnderlyingTypeFullyQualifiedName == other.UnderlyingTypeFullyQualifiedName &&
39+
// let's not include this so we don't rerun the pipeline TypeAsWritten == other.TypeAsWritten &&
40+
IsNetObject == other.IsNetObject;
41+
42+
public override string ToString() =>
43+
FullyQualifiedName;
44+
45+
public override int GetHashCode() =>
46+
Name.GetHashCode() ^
47+
Namespace.GetHashCode() ^
48+
FullyQualifiedName.GetHashCode() ^
49+
((Subtypes != null) ? Subtypes.GetHashCode() : 1) ^
50+
((UnderlyingTypeFullyQualifiedName != null) ? UnderlyingTypeFullyQualifiedName.GetHashCode() : 1) ^
51+
IsNetObject.GetHashCode();
52+
53+
public static bool operator ==(NetworkParameterTypeModel left, NetworkParameterTypeModel right) =>
54+
left.Equals(right);
55+
56+
public static bool operator !=(NetworkParameterTypeModel left, NetworkParameterTypeModel right) =>
57+
!left.Equals(right);
58+
59+
}
60+
}

0 commit comments

Comments
 (0)