Skip to content

Commit eabb24a

Browse files
Generate a global shape provider per compilation. (#57)
1 parent 20dc8b2 commit eabb24a

31 files changed

+244
-303
lines changed

src/PolyType.SourceGenerator/Helpers/RoslynHelpers.IncrementalValues.cs

+52-19
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,25 @@ internal static partial class RoslynHelpers
1717
/// <summary>
1818
/// Replacement for <see cref="SyntaxValueProvider.ForAttributeWithMetadataName" /> that handles generic attributes correctly.
1919
/// </summary>
20-
public static IncrementalValuesProvider<TypeWithAttributeDeclarationContext> ForTypesWithAttributeDeclaration(
21-
this SyntaxValueProvider provider, string attributeFullyQualifiedName,
20+
public static IncrementalValuesProvider<TypeWithAttributeDeclarationContext> ForTypesWithAttributeDeclarations(
21+
this SyntaxValueProvider provider,
22+
string[] attributeFullyQualifiedNames,
2223
Func<BaseTypeDeclarationSyntax, CancellationToken, bool> predicate)
2324
{
24-
NameSyntax attributeNameSyntax = SyntaxFactory.ParseName(attributeFullyQualifiedName);
25-
string attributeName = GetTypeName(attributeNameSyntax, out int attributeArity);
26-
ImmutableArray<string> attributeNamespace = GetNamespaceTokens(attributeNameSyntax);
27-
string? attributeNameMinusSuffix = attributeName.EndsWith("Attribute", StringComparison.Ordinal) ? attributeName[..^"Attribute".Length] : null;
25+
Debug.Assert(attributeFullyQualifiedNames.Length is > 0 and <= 3, "Does not optimize for large lists of attributes.");
26+
ParseAttributeFullyQualifiedNames(attributeFullyQualifiedNames, out var attributeData, out var attributeSyntaxNodeCandidates);
2827

2928
return provider.CreateSyntaxProvider(
3029
predicate: (SyntaxNode node, CancellationToken token) => node is BaseTypeDeclarationSyntax typeDecl && IsAnnotatedTypeDeclaration(typeDecl, token),
3130
transform: Transform)
3231
.Where(ctx => ctx.Type != null)
3332
.GroupBy(
34-
keySelector: value => value.Type,
35-
resultSelector: static (key, values) =>
36-
new TypeWithAttributeDeclarationContext
37-
{
38-
TypeSymbol = (ITypeSymbol)key!,
39-
Declarations = values.Select(v => (v.Syntax, v.Model)).ToImmutableArray()
33+
keySelector: value => value.Type,
34+
resultSelector: static (key, values) =>
35+
new TypeWithAttributeDeclarationContext
36+
{
37+
TypeSymbol = (ITypeSymbol)key!,
38+
Declarations = values.Select(v => (v.Syntax, v.Model)).ToImmutableArray()
4039
},
4140

4241
keyComparer: SymbolEqualityComparer.Default);
@@ -48,9 +47,12 @@ bool IsAnnotatedTypeDeclaration(BaseTypeDeclarationSyntax typeDecl, Cancellation
4847
foreach (AttributeSyntax attribute in attributeList.Attributes)
4948
{
5049
string name = GetTypeName(attribute.Name, out int arity);
51-
if ((name == attributeName || name == attributeNameMinusSuffix) && arity == attributeArity)
50+
foreach (var candidate in attributeSyntaxNodeCandidates)
5251
{
53-
return predicate(typeDecl, token);
52+
if (candidate.name == name && candidate.arity == arity)
53+
{
54+
return predicate(typeDecl, token);
55+
}
5456
}
5557
}
5658
}
@@ -65,12 +67,17 @@ bool IsAnnotatedTypeDeclaration(BaseTypeDeclarationSyntax typeDecl, Cancellation
6567

6668
foreach (AttributeData attrData in typeSymbol.GetAttributes())
6769
{
68-
if (attrData.AttributeClass is INamedTypeSymbol attributeType &&
69-
attributeType.Name == attributeName &&
70-
attributeType.Arity == attributeArity &&
71-
attributeType.ContainingNamespace.MatchesNamespace(attributeNamespace))
70+
if (attrData.AttributeClass is INamedTypeSymbol attributeType)
7271
{
73-
return (typeSymbol, typeDecl, ctx.SemanticModel);
72+
foreach (var (attributeNamespace, attributeName, attributeArity) in attributeData)
73+
{
74+
if (attributeType.Name == attributeName &&
75+
attributeType.Arity == attributeArity &&
76+
attributeType.ContainingNamespace.MatchesNamespace(attributeNamespace))
77+
{
78+
return (typeSymbol, typeDecl, ctx.SemanticModel);
79+
}
80+
}
7481
}
7582
}
7683

@@ -130,6 +137,32 @@ void Traverse(NameSyntax current)
130137
}
131138
}
132139
}
140+
141+
static void ParseAttributeFullyQualifiedNames(
142+
string[] attributeFullyQualifiedNames,
143+
out (ImmutableArray<string> Namespace, string Name, int Arity)[] attributeData,
144+
out (string name, int arity)[] attributeSyntaxNodeCandidates)
145+
{
146+
attributeData = new (ImmutableArray<string> Namespace, string Name, int Arity)[attributeFullyQualifiedNames.Length];
147+
List<(string name, int arity)> attributeSyntaxNodeCandidateList = new();
148+
int i = 0;
149+
150+
foreach (string attributeFqn in attributeFullyQualifiedNames)
151+
{
152+
NameSyntax attributeNameSyntax = SyntaxFactory.ParseName(attributeFqn);
153+
string attributeName = GetTypeName(attributeNameSyntax, out int attributeArity);
154+
155+
attributeSyntaxNodeCandidateList.Add((attributeName, attributeArity));
156+
if (attributeName.EndsWith("Attribute", StringComparison.Ordinal))
157+
{
158+
attributeSyntaxNodeCandidateList.Add((attributeName[..^"Attribute".Length], attributeArity));
159+
}
160+
161+
attributeData[i++] = (GetNamespaceTokens(attributeNameSyntax), attributeName, attributeArity);
162+
}
163+
164+
attributeSyntaxNodeCandidates = attributeSyntaxNodeCandidateList.ToArray();
165+
}
133166
}
134167

135168
// Cf. https://github.com/dotnet/roslyn/issues/72667

src/PolyType.SourceGenerator/Model/TypeDeclarationModel.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ public record TypeDeclarationModel
1010
public required ImmutableEquatableArray<string> ContainingTypes { get; init; }
1111
public required string SourceFilenamePrefix { get; init; }
1212
public required string? Namespace { get; init; }
13-
public required bool IsValidTypeDeclaration { get; init; }
13+
public required bool ImplementsITypeShapeProvider { get; init; }
14+
public required ImmutableEquatableSet<TypeId> ShapeableOfTImplementations { get; init; }
1415
}

src/PolyType.SourceGenerator/Model/TypeId.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace PolyType.SourceGenerator.Model;
88
public readonly struct TypeId : IEquatable<TypeId>
99
{
1010
public required string FullyQualifiedName { get; init; }
11-
public required string GeneratedPropertyName { get; init; }
11+
public required string TypeIdentifier { get; init; }
1212
public required bool IsValueType { get; init; }
1313
public required SpecialType SpecialType { get; init; }
1414

src/PolyType.SourceGenerator/Model/TypeShapeModel.cs

-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,4 @@ namespace PolyType.SourceGenerator.Model;
55
public abstract record TypeShapeModel
66
{
77
public required TypeId Type { get; init; }
8-
public required bool EmitGenericTypeShapeProviderImplementation { get; init; }
98
}

src/PolyType.SourceGenerator/Model/TypeShapeProviderModel.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ namespace PolyType.SourceGenerator.Model;
44

55
public sealed record TypeShapeProviderModel
66
{
7-
public required TypeDeclarationModel Declaration { get; init; }
7+
public required TypeDeclarationModel ProviderDeclaration { get; init; }
88
public required ImmutableEquatableDictionary<TypeId, TypeShapeModel> ProvidedTypes { get; init; }
99
public required ImmutableEquatableSet<EquatableDiagnostic> Diagnostics { get; init; }
10-
public required ImmutableEquatableArray<TypeDeclarationModel> GenerateShapeTypes { get; init; }
10+
public required ImmutableEquatableArray<TypeDeclarationModel> AnnotatedTypes { get; init; }
1111
}

src/PolyType.SourceGenerator/Parser/Parser.ModelMapper.cs

+1-9
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,20 @@ namespace PolyType.SourceGenerator;
77

88
public sealed partial class Parser
99
{
10-
private TypeShapeModel MapModel(TypeId typeId, TypeDataModel model, bool isGeneratedViaWitnessType)
10+
private TypeShapeModel MapModel(TypeId typeId, TypeDataModel model)
1111
{
12-
bool emitGenericProviderImplementation = isGeneratedViaWitnessType && model.IsRootType;
1312
return model switch
1413
{
1514
EnumDataModel enumModel => new EnumShapeModel
1615
{
1716
Type = typeId,
1817
UnderlyingType = CreateTypeId(enumModel.UnderlyingType),
19-
EmitGenericTypeShapeProviderImplementation = emitGenericProviderImplementation,
2018
},
2119

2220
NullableDataModel nullableModel => new NullableShapeModel
2321
{
2422
Type = typeId,
2523
ElementType = CreateTypeId(nullableModel.ElementType),
26-
EmitGenericTypeShapeProviderImplementation = emitGenericProviderImplementation,
2724
},
2825

2926
EnumerableDataModel enumerableModel => new EnumerableShapeModel
@@ -61,7 +58,6 @@ enumerableModel.ConstructionStrategy is CollectionModelConstructionStrategy.List
6158

6259
Kind = enumerableModel.EnumerableKind,
6360
Rank = enumerableModel.Rank,
64-
EmitGenericTypeShapeProviderImplementation = emitGenericProviderImplementation,
6561
ElementTypeContainsNullableAnnotations = enumerableModel.ElementType.ContainsNullabilityAnnotations(),
6662
},
6763

@@ -94,7 +90,6 @@ dictionaryModel.ConstructionStrategy is CollectionModelConstructionStrategy.Muta
9490
StaticFactoryMethod = dictionaryModel.FactoryMethod is { IsStatic: true } m ? m.GetFullyQualifiedName() : null,
9591
IsTupleEnumerableFactory = dictionaryModel.ConstructionStrategy is CollectionModelConstructionStrategy.TupleEnumerable,
9692
Kind = dictionaryModel.DictionaryKind,
97-
EmitGenericTypeShapeProviderImplementation = emitGenericProviderImplementation,
9893
CtorRequiresDictionaryConversion =
9994
dictionaryModel.ConstructionStrategy is CollectionModelConstructionStrategy.Dictionary &&
10095
!IsFactoryAcceptingIEnumerable(dictionaryModel.FactoryMethod),
@@ -118,7 +113,6 @@ dictionaryModel.ConstructionStrategy is CollectionModelConstructionStrategy.Dict
118113
IsValueTupleType = false,
119114
IsTupleType = false,
120115
IsRecordType = model.Type.IsRecord,
121-
EmitGenericTypeShapeProviderImplementation = emitGenericProviderImplementation,
122116
},
123117

124118
TupleDataModel tupleModel => new ObjectShapeModel
@@ -132,7 +126,6 @@ dictionaryModel.ConstructionStrategy is CollectionModelConstructionStrategy.Dict
132126
IsValueTupleType = tupleModel.IsValueTuple,
133127
IsTupleType = true,
134128
IsRecordType = false,
135-
EmitGenericTypeShapeProviderImplementation = emitGenericProviderImplementation,
136129
},
137130

138131
_ => new ObjectShapeModel
@@ -143,7 +136,6 @@ dictionaryModel.ConstructionStrategy is CollectionModelConstructionStrategy.Dict
143136
IsValueTupleType = false,
144137
IsTupleType = false,
145138
IsRecordType = false,
146-
EmitGenericTypeShapeProviderImplementation = emitGenericProviderImplementation,
147139
}
148140
};
149141

0 commit comments

Comments
 (0)