@@ -208,35 +208,32 @@ public static bool EnumerableGenericTypeIs(this ITypeSymbol enumerable, Generato
208208
209209 public static string GloballyQualified ( this ISymbol typeSymbol )
210210 {
211- // Special handling for System.Nullable<> generic type definition
212- // When Roslyn encounters System.Nullable< >, it displays it as "T?" which is not valid C# syntax
213- if ( typeSymbol is INamedTypeSymbol namedTypeSymbol )
211+ // Handle open generic types where type arguments are type parameters
212+ // This prevents invalid C# like List<T >, Dictionary<TKey, TValue>, T? where type parameters are undefined
213+ if ( typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol . IsGenericType )
214214 {
215- // Check if this is System.Nullable<T> where T is unbound/type parameter
216- if ( namedTypeSymbol . SpecialType == SpecialType . System_Nullable_T ||
217- namedTypeSymbol . ConstructedFrom ? . SpecialType == SpecialType . System_Nullable_T )
215+ // Check if this is an unbound generic type or has type parameter arguments
216+ bool hasTypeParameters = namedTypeSymbol . TypeArguments . Any ( t => t . TypeKind == TypeKind . TypeParameter ) ;
217+ bool isUnboundGeneric = namedTypeSymbol . IsUnboundGenericType ;
218+
219+ if ( hasTypeParameters || isUnboundGeneric )
218220 {
219- // For the unbound generic case like System.Nullable<> or Nullable<T> where T is a type parameter
220- if ( namedTypeSymbol . TypeArguments . Length == 1 &&
221- namedTypeSymbol . TypeArguments [ 0 ] . TypeKind == TypeKind . TypeParameter )
221+ // Special case for System.Nullable<> - Roslyn displays it as "T?" even for open generic
222+ if ( namedTypeSymbol . SpecialType == SpecialType . System_Nullable_T ||
223+ namedTypeSymbol . ConstructedFrom ? . SpecialType == SpecialType . System_Nullable_T )
222224 {
223225 return "global::System.Nullable<>" ;
224226 }
227+
228+ // General case for other open generic types
229+ var typeBuilder = new StringBuilder ( typeSymbol . ToDisplayString ( DisplayFormats . FullyQualifiedNonGenericWithGlobalPrefix ) ) ;
230+ typeBuilder . Append ( '<' ) ;
231+ typeBuilder . Append ( new string ( ',' , namedTypeSymbol . TypeArguments . Length - 1 ) ) ;
232+ typeBuilder . Append ( '>' ) ;
233+
234+ return typeBuilder . ToString ( ) ;
225235 }
226236 }
227-
228- // Only generate open generic form for types with unresolved type parameters
229- // This ensures we get BaseClass<> for generic definitions but List<int> for constructed types
230- if ( typeSymbol is INamedTypeSymbol { IsGenericType : true } namedTypeSymbol2 &&
231- namedTypeSymbol2 . TypeArguments . Any ( t => t . TypeKind == TypeKind . TypeParameter ) )
232- {
233- var typeBuilder = new StringBuilder ( typeSymbol . ToDisplayString ( DisplayFormats . FullyQualifiedNonGenericWithGlobalPrefix ) ) ;
234- typeBuilder . Append ( '<' ) ;
235- typeBuilder . Append ( new string ( ',' , namedTypeSymbol2 . TypeArguments . Length - 1 ) ) ;
236- typeBuilder . Append ( '>' ) ;
237-
238- return typeBuilder . ToString ( ) ;
239- }
240237
241238 return typeSymbol . ToDisplayString ( DisplayFormats . FullyQualifiedGenericWithGlobalPrefix ) ;
242239 }
0 commit comments