diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Utilities/TypeSymbolExtensions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Utilities/TypeSymbolExtensions.cs index 15a021980a9..61d547b0106 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Utilities/TypeSymbolExtensions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Utilities/TypeSymbolExtensions.cs @@ -13,6 +13,7 @@ internal static class TypeSymbolExtensions { private const string GlobalPrefix = "global::"; private const string NullableTypeName = "System.Nullable"; + private const string TupleTypeName = "System.ValueTuple"; public static bool IsSameType(this INamedTypeSymbol symbol, CSharpType type) { @@ -105,38 +106,46 @@ public static string GetFullyQualifiedName(this ITypeSymbol typeSymbol) { return GetFullyQualifiedName(arrayTypeSymbol.ElementType) + "[]"; } - - // Handle generic types - if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.IsGenericType) + // Handle tuples & generic types + if (typeSymbol is INamedTypeSymbol namedTypeSymbol) { - // Handle nullable types - if (typeSymbol.NullableAnnotation == NullableAnnotation.Annotated && !IsCollectionType(namedTypeSymbol)) + if (typeSymbol.IsTupleType) { - var argTypeSymbol = namedTypeSymbol.TypeArguments.FirstOrDefault(); + string[] elementTypes = [.. namedTypeSymbol.TupleElements.Select(e => GetFullyQualifiedName(e.Type))]; + return $"{TupleTypeName}`{elementTypes.Length}[{string.Join(", ", elementTypes)}]"; + } - if (argTypeSymbol != null) + if (namedTypeSymbol.IsGenericType) + { + // Handle nullable types + if (typeSymbol.NullableAnnotation == NullableAnnotation.Annotated && !IsCollectionType(namedTypeSymbol)) { - // If the argument type is an error type, then fall back to using the ToString of the arg type symbol. This means that the - // arg may not be fully qualified, but it is better than not having any type information at all. - if (argTypeSymbol.TypeKind == TypeKind.Error) + var argTypeSymbol = namedTypeSymbol.TypeArguments.FirstOrDefault(); + + if (argTypeSymbol != null) { - return $"{NullableTypeName}`1[{argTypeSymbol}]"; + // If the argument type is an error type, then fall back to using the ToString of the arg type symbol. This means that the + // arg may not be fully qualified, but it is better than not having any type information at all. + if (argTypeSymbol.TypeKind == TypeKind.Error) + { + return $"{NullableTypeName}`1[{argTypeSymbol}]"; + } + + string[] typeArguments = [.. namedTypeSymbol.TypeArguments.Select(arg => "[" + GetFullyQualifiedName(arg) + "]")]; + return $"{NullableTypeName}`{namedTypeSymbol.TypeArguments.Length}[{string.Join(", ", typeArguments)}]"; } - - string[] typeArguments = [.. namedTypeSymbol.TypeArguments.Select(arg => "[" + GetFullyQualifiedName(arg) + "]")]; - return $"{NullableTypeName}`{namedTypeSymbol.TypeArguments.Length}[{string.Join(", ", typeArguments)}]"; } - } - else if (namedTypeSymbol.TypeArguments.Length > 0 && !IsCollectionType(namedTypeSymbol)) - { - return GetNonNullableGenericTypeName(namedTypeSymbol); - } + else if (namedTypeSymbol.TypeArguments.Length > 0 && !IsCollectionType(namedTypeSymbol)) + { + return GetNonNullableGenericTypeName(namedTypeSymbol); + } - var typeNameSpan = namedTypeSymbol.ConstructedFrom.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).AsSpan(); - var start = typeNameSpan.IndexOf(':') + 2; - var end = typeNameSpan.IndexOf('<'); - typeNameSpan = typeNameSpan.Slice(start, end - start); - return $"{typeNameSpan}`{namedTypeSymbol.TypeArguments.Length}"; + var typeNameSpan = namedTypeSymbol.ConstructedFrom.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).AsSpan(); + var start = typeNameSpan.IndexOf(':') + 2; + var end = typeNameSpan.IndexOf('<'); + typeNameSpan = typeNameSpan.Slice(start, end - start); + return $"{typeNameSpan}`{namedTypeSymbol.TypeArguments.Length}"; + } } // Default to fully qualified name diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/NamedTypeSymbolProviders/NamedTypeSymbolProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/NamedTypeSymbolProviders/NamedTypeSymbolProviderTests.cs index 3adfab3ca34..84eb3104bec 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/NamedTypeSymbolProviders/NamedTypeSymbolProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/NamedTypeSymbolProviders/NamedTypeSymbolProviderTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.TypeSpec.Generator.Expressions; @@ -96,6 +97,7 @@ public void ValidateProperties() [TestCase(typeof(SomeEnum), true)] [TestCase(typeof(SomeEnum?), true)] [TestCase(typeof(IDictionary))] + [TestCase(typeof((List Values, string Foo, int Bar)))] public void ValidatePropertyTypes(Type propertyType, bool isEnum = false) { // setup