Skip to content

Commit 58c4a3a

Browse files
fix: handle tuple types in custom code (#7482)
This PR fixes an issue in the NamedTypeSymbolProvider when loading tuple types. Previously, the generator would crash with ``The given assembly name was invalid.`` when attempting to load the tuple type. contributes to : Azure/azure-sdk-for-net#50288
1 parent 9f43a52 commit 58c4a3a

File tree

2 files changed

+35
-24
lines changed

2 files changed

+35
-24
lines changed

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Utilities/TypeSymbolExtensions.cs

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ internal static class TypeSymbolExtensions
1313
{
1414
private const string GlobalPrefix = "global::";
1515
private const string NullableTypeName = "System.Nullable";
16+
private const string TupleTypeName = "System.ValueTuple";
1617

1718
public static bool IsSameType(this INamedTypeSymbol symbol, CSharpType type)
1819
{
@@ -105,38 +106,46 @@ public static string GetFullyQualifiedName(this ITypeSymbol typeSymbol)
105106
{
106107
return GetFullyQualifiedName(arrayTypeSymbol.ElementType) + "[]";
107108
}
108-
109-
// Handle generic types
110-
if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.IsGenericType)
109+
// Handle tuples & generic types
110+
if (typeSymbol is INamedTypeSymbol namedTypeSymbol)
111111
{
112-
// Handle nullable types
113-
if (typeSymbol.NullableAnnotation == NullableAnnotation.Annotated && !IsCollectionType(namedTypeSymbol))
112+
if (typeSymbol.IsTupleType)
114113
{
115-
var argTypeSymbol = namedTypeSymbol.TypeArguments.FirstOrDefault();
114+
string[] elementTypes = [.. namedTypeSymbol.TupleElements.Select(e => GetFullyQualifiedName(e.Type))];
115+
return $"{TupleTypeName}`{elementTypes.Length}[{string.Join(", ", elementTypes)}]";
116+
}
116117

117-
if (argTypeSymbol != null)
118+
if (namedTypeSymbol.IsGenericType)
119+
{
120+
// Handle nullable types
121+
if (typeSymbol.NullableAnnotation == NullableAnnotation.Annotated && !IsCollectionType(namedTypeSymbol))
118122
{
119-
// If the argument type is an error type, then fall back to using the ToString of the arg type symbol. This means that the
120-
// arg may not be fully qualified, but it is better than not having any type information at all.
121-
if (argTypeSymbol.TypeKind == TypeKind.Error)
123+
var argTypeSymbol = namedTypeSymbol.TypeArguments.FirstOrDefault();
124+
125+
if (argTypeSymbol != null)
122126
{
123-
return $"{NullableTypeName}`1[{argTypeSymbol}]";
127+
// If the argument type is an error type, then fall back to using the ToString of the arg type symbol. This means that the
128+
// arg may not be fully qualified, but it is better than not having any type information at all.
129+
if (argTypeSymbol.TypeKind == TypeKind.Error)
130+
{
131+
return $"{NullableTypeName}`1[{argTypeSymbol}]";
132+
}
133+
134+
string[] typeArguments = [.. namedTypeSymbol.TypeArguments.Select(arg => "[" + GetFullyQualifiedName(arg) + "]")];
135+
return $"{NullableTypeName}`{namedTypeSymbol.TypeArguments.Length}[{string.Join(", ", typeArguments)}]";
124136
}
125-
126-
string[] typeArguments = [.. namedTypeSymbol.TypeArguments.Select(arg => "[" + GetFullyQualifiedName(arg) + "]")];
127-
return $"{NullableTypeName}`{namedTypeSymbol.TypeArguments.Length}[{string.Join(", ", typeArguments)}]";
128137
}
129-
}
130-
else if (namedTypeSymbol.TypeArguments.Length > 0 && !IsCollectionType(namedTypeSymbol))
131-
{
132-
return GetNonNullableGenericTypeName(namedTypeSymbol);
133-
}
138+
else if (namedTypeSymbol.TypeArguments.Length > 0 && !IsCollectionType(namedTypeSymbol))
139+
{
140+
return GetNonNullableGenericTypeName(namedTypeSymbol);
141+
}
134142

135-
var typeNameSpan = namedTypeSymbol.ConstructedFrom.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).AsSpan();
136-
var start = typeNameSpan.IndexOf(':') + 2;
137-
var end = typeNameSpan.IndexOf('<');
138-
typeNameSpan = typeNameSpan.Slice(start, end - start);
139-
return $"{typeNameSpan}`{namedTypeSymbol.TypeArguments.Length}";
143+
var typeNameSpan = namedTypeSymbol.ConstructedFrom.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).AsSpan();
144+
var start = typeNameSpan.IndexOf(':') + 2;
145+
var end = typeNameSpan.IndexOf('<');
146+
typeNameSpan = typeNameSpan.Slice(start, end - start);
147+
return $"{typeNameSpan}`{namedTypeSymbol.TypeArguments.Length}";
148+
}
140149
}
141150

142151
// Default to fully qualified name

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/NamedTypeSymbolProviders/NamedTypeSymbolProviderTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Configuration;
67
using System.Linq;
78
using Microsoft.CodeAnalysis;
89
using Microsoft.TypeSpec.Generator.Expressions;
@@ -96,6 +97,7 @@ public void ValidateProperties()
9697
[TestCase(typeof(SomeEnum), true)]
9798
[TestCase(typeof(SomeEnum?), true)]
9899
[TestCase(typeof(IDictionary<string, SomeEnum>))]
100+
[TestCase(typeof((List<PropertyType> Values, string Foo, int Bar)))]
99101
public void ValidatePropertyTypes(Type propertyType, bool isEnum = false)
100102
{
101103
// setup

0 commit comments

Comments
 (0)