Skip to content

Commit a6d4a71

Browse files
committed
Moved source generation for ObjectFactoryAttribute to its own Source Generator.
1 parent 6527058 commit a6d4a71

File tree

88 files changed

+1519
-495
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+1519
-495
lines changed
Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
using System;
2-
using System.Diagnostics.CodeAnalysis;
3-
using MessagePack;
4-
using Thinktecture.Formatters;
52

63
namespace Thinktecture;
74

@@ -11,20 +8,16 @@ namespace Thinktecture;
118
SwitchMethods = SwitchMapMethodsGeneration.DefaultWithPartialOverloads,
129
MapMethods = SwitchMapMethodsGeneration.DefaultWithPartialOverloads)]
1310
[ObjectFactory<string>(UseForSerialization = SerializationFrameworks.All)]
14-
[MessagePackFormatter(typeof(ThinktectureMessagePackFormatter<TextOrNumberSerializableWithFormatter, string, ValidationError>))]
15-
public partial class TextOrNumberSerializableWithFormatter :
16-
IObjectFactory<TextOrNumberSerializableWithFormatter, string, ValidationError>, // For deserialization
17-
IConvertible<string>, // For serialization
18-
IParsable<TextOrNumberSerializableWithFormatter> // For Minimal API and ASP.NET Core model binding validation
11+
public partial class TextOrNumberSerializableWithFormatter
1912
{
20-
// For serialization
13+
// For serialization (implementation of IConvertible<string>)
2114
public string ToValue()
2215
{
2316
return Switch(text: t => $"Text|{t}",
2417
number: n => $"Number|{n}");
2518
}
2619

27-
// For deserialization
20+
// For deserialization (implementation of IObjectFactory<TextOrNumberSerializableWithFormatter, string, ValidationError>)
2821
public static ValidationError? Validate(string? value, IFormatProvider? provider, out TextOrNumberSerializableWithFormatter? item)
2922
{
3023
if (String.IsNullOrWhiteSpace(value))
@@ -54,29 +47,4 @@ public string ToValue()
5447
item = null;
5548
return new ValidationError("Invalid format");
5649
}
57-
58-
public static TextOrNumberSerializableWithFormatter Parse(string s, IFormatProvider? provider)
59-
{
60-
var validationError = Validate(s, provider, out var result);
61-
62-
if (validationError is null)
63-
return result!;
64-
65-
throw new FormatException(validationError.Message);
66-
}
67-
68-
public static bool TryParse(
69-
[NotNullWhen(true)] string? s,
70-
IFormatProvider? provider,
71-
[MaybeNullWhen(false)] out TextOrNumberSerializableWithFormatter result)
72-
{
73-
if (s is null)
74-
{
75-
result = null;
76-
return false;
77-
}
78-
79-
var validationError = Validate(s, provider, out result!);
80-
return validationError is null;
81-
}
8250
}
Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Diagnostics.CodeAnalysis;
32
using System.Text.Json.Serialization;
43
using Thinktecture.Text.Json.Serialization;
54

@@ -11,19 +10,16 @@ namespace Thinktecture.Unions;
1110
MapMethods = SwitchMapMethodsGeneration.DefaultWithPartialOverloads)]
1211
[ObjectFactory<string>(UseForSerialization = SerializationFrameworks.All)]
1312
[JsonConverter(typeof(ThinktectureJsonConverterFactory))] // Optional, ThinktectureJsonConverterFactory can be registered with JsonSerializerOptions
14-
public partial class TextOrNumberSerializable :
15-
IObjectFactory<TextOrNumberSerializable, string, ValidationError>, // For deserialization
16-
IConvertible<string>, // For serialization
17-
IParsable<TextOrNumberSerializable> // For Minimal API and ASP.NET Core model binding validation
13+
public partial class TextOrNumberSerializable
1814
{
19-
// For serialization
15+
// For serialization (implementation of IConvertible<string>)
2016
public string ToValue()
2117
{
2218
return Switch(text: t => $"Text|{t}",
2319
number: n => $"Number|{n}");
2420
}
2521

26-
// For deserialization
22+
// For deserialization (implementation of IObjectFactory<TextOrNumberSerializable, string, ValidationError>)
2723
public static ValidationError? Validate(string? value, IFormatProvider? provider, out TextOrNumberSerializable? item)
2824
{
2925
if (String.IsNullOrWhiteSpace(value))
@@ -53,29 +49,4 @@ public string ToValue()
5349
item = null;
5450
return new ValidationError("Invalid format");
5551
}
56-
57-
public static TextOrNumberSerializable Parse(string s, IFormatProvider? provider)
58-
{
59-
var validationError = Validate(s, provider, out var result);
60-
61-
if (validationError is null)
62-
return result!;
63-
64-
throw new FormatException(validationError.Message);
65-
}
66-
67-
public static bool TryParse(
68-
[NotNullWhen(true)] string? s,
69-
IFormatProvider? provider,
70-
[MaybeNullWhen(false)] out TextOrNumberSerializable result)
71-
{
72-
if (s is null)
73-
{
74-
result = null;
75-
return false;
76-
}
77-
78-
var validationError = Validate(s, provider, out result!);
79-
return validationError is null;
80-
}
8152
}

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AdHocUnions/AdHocUnionCodeGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ private void GenerateUnion(CancellationToken cancellationToken)
5353

5454
_sb.Append(@"
5555
[global::System.Diagnostics.CodeAnalysis.SuppressMessage(""ThinktectureRuntimeExtensionsAnalyzer"", ""TTRESG1000:Internal Thinktecture.Runtime.Extensions API usage"")]
56-
").Append(_state.IsReferenceType ? "sealed " : "readonly ").Append("partial ").Append(_state.IsReferenceType ? "class" : "struct").Append(" ").Append(_state.Name).Append(" :");
56+
").Append(_state.IsReferenceType ? "sealed " : "readonly ").Append("partial ").AppendTypeKind(_state).Append(" ").Append(_state.Name).Append(" :");
5757

5858
if (!_state.IsRefStruct)
5959
{

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AdHocUnions/AdHocUnionMemberTypeState.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
namespace Thinktecture.CodeAnalysis.AdHocUnions;
22

3-
public sealed class AdHocUnionMemberTypeState : IEquatable<AdHocUnionMemberTypeState>, IMemberInformation, ITypeMinimallyQualified, IHashCodeComputable
3+
public sealed class AdHocUnionMemberTypeState
4+
: IEquatable<AdHocUnionMemberTypeState>,
5+
IMemberInformation,
6+
ITypeMinimallyQualified,
7+
IHashCodeComputable
48
{
59
public string TypeFullyQualified { get; }
610
public string TypeMinimallyQualified { get; }
@@ -16,6 +20,8 @@ public sealed class AdHocUnionMemberTypeState : IEquatable<AdHocUnionMemberTypeS
1620
public string BackingFieldName { get; }
1721
public AdHocUnionMemberTypeSetting Setting { get; }
1822

23+
public bool IsRecord => false;
24+
1925
public AdHocUnionMemberTypeState(
2026
string name,
2127
string defaultName,

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AdHocUnions/AdHocUnionSourceGenState.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
namespace Thinktecture.CodeAnalysis.AdHocUnions;
22

3-
public sealed class AdHocUnionSourceGenState : ITypeInformation, IEquatable<AdHocUnionSourceGenState>
3+
public sealed class AdHocUnionSourceGenState
4+
: ITypeInformation,
5+
IEquatable<AdHocUnionSourceGenState>
46
{
57
public string? Namespace { get; }
68
public string Name { get; }
@@ -13,10 +15,12 @@ public sealed class AdHocUnionSourceGenState : ITypeInformation, IEquatable<AdHo
1315
public bool IsNullableStruct { get; }
1416
public bool IsRefStruct { get; }
1517
public bool IsEqualWithReferenceEquality => false;
18+
19+
public bool IsRecord => false;
1620
public bool DisallowsDefaultValue => true;
21+
public int NumberOfGenerics => 0;
1722

1823
public IReadOnlyList<AdHocUnionMemberTypeState> MemberTypes { get; }
19-
public IReadOnlyList<string> GenericsFullyQualified => [];
2024
public AdHocUnionSettings Settings { get; }
2125

2226
public AdHocUnionSourceGenState(

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AttributeInfo.cs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Thinktecture.CodeAnalysis;
66
public bool HasJsonConverterAttribute { get; }
77
public bool HasNewtonsoftJsonConverterAttribute { get; }
88
public bool HasMessagePackFormatterAttribute { get; }
9-
public ImmutableArray<DesiredFactory> DesiredFactories { get; }
9+
public ImmutableArray<ObjectFactoryState> ObjectFactories { get; }
1010
public ValidationErrorState ValidationError { get; }
1111
public string? KeyMemberComparerAccessor { get; }
1212
public string? KeyMemberEqualityComparerAccessor { get; }
@@ -16,7 +16,7 @@ private AttributeInfo(
1616
bool hasJsonConverterAttribute,
1717
bool hasNewtonsoftJsonConverterAttribute,
1818
bool hasMessagePackFormatterAttribute,
19-
ImmutableArray<DesiredFactory> desiredFactories,
19+
ImmutableArray<ObjectFactoryState> objectFactories,
2020
ValidationErrorState validationError,
2121
string? keyMemberComparerAccessor,
2222
string? keyMemberEqualityComparerAccessor)
@@ -25,22 +25,33 @@ private AttributeInfo(
2525
HasJsonConverterAttribute = hasJsonConverterAttribute;
2626
HasNewtonsoftJsonConverterAttribute = hasNewtonsoftJsonConverterAttribute;
2727
HasMessagePackFormatterAttribute = hasMessagePackFormatterAttribute;
28-
DesiredFactories = desiredFactories;
28+
ObjectFactories = objectFactories;
2929
ValidationError = validationError;
3030
KeyMemberComparerAccessor = keyMemberComparerAccessor;
3131
KeyMemberEqualityComparerAccessor = keyMemberEqualityComparerAccessor;
3232
}
3333

34-
public static string? TryCreate(INamedTypeSymbol type, out AttributeInfo info)
34+
public static string? TryCreate(
35+
INamedTypeSymbol type,
36+
out AttributeInfo info)
37+
{
38+
return TryCreate(type, out info, out _);
39+
}
40+
41+
public static string? TryCreate(
42+
INamedTypeSymbol type,
43+
out AttributeInfo info,
44+
out AttributeData? thinktectureComponentAttribute)
3545
{
3646
var hasStructLayoutAttribute = false;
3747
var hasJsonConverterAttribute = false;
3848
var hasNewtonsoftJsonConverterAttribute = false;
3949
var hasMessagePackFormatterAttribute = false;
4050
var validationError = ValidationErrorState.Default;
41-
var valueObjectFactories = ImmutableArray<DesiredFactory>.Empty;
51+
var valueObjectFactories = ImmutableArray<ObjectFactoryState>.Empty;
4252
string? keyMemberComparerAccessor = null;
4353
string? keyMemberEqualityComparerAccessor = null;
54+
thinktectureComponentAttribute = null;
4455
var numberOfSourceGenAttributes = 0;
4556

4657
foreach (var attribute in type.GetAttributes())
@@ -67,7 +78,7 @@ private AttributeInfo(
6778
else if (attribute.AttributeClass.IsObjectFactoryAttribute())
6879
{
6980
var useForSerialization = attribute.FindUseForSerialization();
70-
var desiredFactory = new DesiredFactory(attribute.AttributeClass.TypeArguments[0], useForSerialization);
81+
var desiredFactory = new ObjectFactoryState(attribute.AttributeClass.TypeArguments[0], useForSerialization);
7182

7283
valueObjectFactories = valueObjectFactories.RemoveAll(static (f, fullTypeName) => f.TypeFullyQualified == fullTypeName, desiredFactory.TypeFullyQualified);
7384
valueObjectFactories = valueObjectFactories.Add(desiredFactory);
@@ -84,17 +95,16 @@ private AttributeInfo(
8495
{
8596
keyMemberEqualityComparerAccessor = attribute.AttributeClass.TypeArguments[0].ToFullyQualifiedDisplayString();
8697
}
87-
else if (attribute.AttributeClass.IsSmartEnumAttribute()
88-
|| attribute.AttributeClass.IsKeyedValueObjectAttribute()
89-
|| attribute.AttributeClass.IsComplexValueObjectAttribute())
98+
else if (attribute.AttributeClass.IsThinktectureComponentAttribute())
9099
{
91100
++numberOfSourceGenAttributes;
101+
thinktectureComponentAttribute = attribute;
92102
}
93103

94104
if (numberOfSourceGenAttributes > 1)
95105
{
96106
info = default;
97-
return "Multiple ValueObject/SmartEnum-attributes found";
107+
return "Multiple ValueObject/SmartEnum/Union-attributes found";
98108
}
99109
}
100110

@@ -120,7 +130,7 @@ public bool Equals(AttributeInfo other)
120130
&& HasJsonConverterAttribute == other.HasJsonConverterAttribute
121131
&& HasNewtonsoftJsonConverterAttribute == other.HasNewtonsoftJsonConverterAttribute
122132
&& HasMessagePackFormatterAttribute == other.HasMessagePackFormatterAttribute
123-
&& DesiredFactories.SequenceEqual(other.DesiredFactories)
133+
&& ObjectFactories.SequenceEqual(other.ObjectFactories)
124134
&& ValidationError.Equals(other.ValidationError)
125135
&& KeyMemberComparerAccessor == other.KeyMemberComparerAccessor
126136
&& KeyMemberEqualityComparerAccessor == other.KeyMemberEqualityComparerAccessor;
@@ -134,7 +144,7 @@ public override int GetHashCode()
134144
hashCode = (hashCode * 397) ^ HasJsonConverterAttribute.GetHashCode();
135145
hashCode = (hashCode * 397) ^ HasNewtonsoftJsonConverterAttribute.GetHashCode();
136146
hashCode = (hashCode * 397) ^ HasMessagePackFormatterAttribute.GetHashCode();
137-
hashCode = (hashCode * 397) ^ DesiredFactories.ComputeHashCode();
147+
hashCode = (hashCode * 397) ^ ObjectFactories.ComputeHashCode();
138148
hashCode = (hashCode * 397) ^ ValidationError.GetHashCode();
139149
hashCode = (hashCode * 397) ^ (KeyMemberComparerAccessor?.GetHashCode() ?? 0);
140150
hashCode = (hashCode * 397) ^ (KeyMemberEqualityComparerAccessor?.GetHashCode() ?? 0);

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/Constants.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public static class DisallowDefaultStructs
5454

5555
public static class Attributes
5656
{
57+
public const string NAMESPACE = "Thinktecture";
58+
5759
public static class Properties
5860
{
5961
public const string KEY_MEMBER_NAME = "KeyMemberName";
@@ -69,7 +71,6 @@ public static class Properties
6971

7072
public static class ValueObject
7173
{
72-
public const string NAMESPACE = "Thinktecture";
7374
public const string KEYED_NAME = "ValueObjectAttribute";
7475
public const string COMPLEX_NAME = "ComplexValueObjectAttribute";
7576
public const string KEYED_FULL_NAME = "Thinktecture.ValueObjectAttribute`1";
@@ -78,20 +79,26 @@ public static class ValueObject
7879

7980
public static class SmartEnum
8081
{
81-
public const string NAMESPACE = "Thinktecture";
8282
public const string NAME = "SmartEnumAttribute";
8383
public const string KEYED_FULL_NAME = "Thinktecture.SmartEnumAttribute`1";
8484
public const string KEYLESS_FULL_NAME = "Thinktecture.SmartEnumAttribute";
8585
}
8686

87-
public static class UseDelegateFromConstructorAttribute
87+
public static class UseDelegateFromConstructor
8888
{
8989
public const string NAME = "UseDelegateFromConstructorAttribute";
9090
}
9191

92+
public static class ObjectFactory
93+
{
94+
public const string NAME = "ObjectFactoryAttribute";
95+
public const string NAME_OBSOLETE = "ValueObjectFactoryAttribute";
96+
public const string FULL_NAME = $"{NAMESPACE}.{NAME}`1";
97+
public const string FULL_NAME_OBSOLETE = $"{NAMESPACE}.{NAME_OBSOLETE}`1";
98+
}
99+
92100
public static class Union
93101
{
94-
public const string NAMESPACE = "Thinktecture";
95102
public const string NAME = "UnionAttribute";
96103
public const string FULL_NAME = "Thinktecture.UnionAttribute";
97104
public const string FULL_NAME_2_TYPES = "Thinktecture.UnionAttribute`2";

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ContainingTypeState.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
namespace Thinktecture.CodeAnalysis;
22

3-
public sealed class ContainingTypeState : IEquatable<ContainingTypeState>, IHashCodeComputable
3+
public sealed class ContainingTypeState
4+
: IEquatable<ContainingTypeState>,
5+
IHasGenerics,
6+
IHashCodeComputable
47
{
58
public string Name { get; }
69
public bool IsReferenceType { get; }

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DefaultMemberState.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ public sealed class DefaultMemberState : IMemberState, IEquatable<DefaultMemberS
1515
public NullableAnnotation NullableAnnotation => _typedMemberState.NullableAnnotation;
1616
public bool IsInterface => _typedMemberState.TypeKind == TypeKind.Interface;
1717

18+
public bool IsRecord => false;
19+
1820
public DefaultMemberState(ITypedMemberState typedMemberState, string name, string argumentName)
1921
{
2022
_typedMemberState = typedMemberState;

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/Diagnostics/ThinktectureRuntimeExtensionsAnalyzer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ private static void AnalyzeUnion(OperationAnalysisContext context)
448448

449449
try
450450
{
451-
if (!attrCreation.Type.IsUnionAttribute())
451+
if (!attrCreation.Type.IsRegularUnionAttribute())
452452
return;
453453

454454
ValidateUnion(context, type);

0 commit comments

Comments
 (0)