@@ -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 } ";
0 commit comments