From 5be78ca88002c8b46545978bfe8aa2dc2862ebd5 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Thu, 17 Apr 2025 21:28:39 +0200 Subject: [PATCH 01/31] Skip prefixing --- .../Generations/Marshalling/Keys.cs | 2 +- .../Generations/Marshalling/Marshaller.cs | 2 +- .../Generations/UnMarshaller.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs index 633bf610..ea8e5171 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs @@ -14,7 +14,7 @@ internal static class KeyMarshaller private const string EnforceRkReference = "isRangeKey"; private static readonly Func MethodName = - TypeExtensions.SuffixedTypeSymbolNameFactory("Keys", SymbolEqualityComparer.IncludeNullability); + TypeExtensions.SuffixedTypeSymbolNameFactory(null, SymbolEqualityComparer.IncludeNullability); private const string PkReference = "partitionKey"; private const string RkReference = "rangeKey"; diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index 119d4c63..01ee3d44 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -9,7 +9,7 @@ internal static partial class Marshaller private const string ClassName = $"_{Constants.DynamoDBGenerator.Marshaller.MarshallMethodName}_"; private const string DataMember = "dataMember"; private const string DictionaryReference = "attributeValues"; - private static readonly Func GetSerializationMethodName = TypeExtensions.SuffixedTypeSymbolNameFactory("_M", SymbolEqualityComparer.IncludeNullability); + private static readonly Func GetSerializationMethodName = TypeExtensions.SuffixedTypeSymbolNameFactory(null, SymbolEqualityComparer.IncludeNullability); private const string ParamReference = "entity"; internal static IEnumerable CreateClass(DynamoDBMarshallerArguments[] arguments, Func getDynamoDbProperties, MarshallerOptions options) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index 04e4b33a..dd84020c 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -10,7 +10,7 @@ public static class UnMarshaller { private const string DataMember = "dataMember"; private const string Dict = "dict"; - private static readonly Func GetDeserializationMethodName = TypeExtensions.SuffixedTypeSymbolNameFactory("_U", SymbolEqualityComparer.IncludeNullability); + private static readonly Func GetDeserializationMethodName = TypeExtensions.SuffixedTypeSymbolNameFactory(null, SymbolEqualityComparer.IncludeNullability); private const string UnMarshallerClass = $"_{Marshaller.UnmarshalMethodName}_"; private const string Value = "attributeValue"; private static IEnumerable<(bool useParentheses, IEnumerable assignments)> Assignments(ITypeSymbol type, (DynamoDbDataMember DDB, string MethodCall, string Name)[] assignments) From 47f4ea2dc90697dac21f2a5e7f380c4b5f27ea75 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Thu, 17 Apr 2025 21:29:09 +0200 Subject: [PATCH 02/31] Use constant over hardcoded --- .../Generations/Marshalling/Marshaller.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index 01ee3d44..5bf4e16b 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -183,8 +183,8 @@ internal static string InvokeMarshallerMethod(ITypeSymbol typeSymbol, string par if (typeSymbol.TypeIdentifier() is UnknownType) return typeSymbol.IsNullable() - ? $"{invocation} switch {{ {{ }} x => new AttributeValue {{ M = x }}, null => null }}" - : $"new AttributeValue {{ M = {invocation} }}"; + ? $"{invocation} switch {{ {{ }} x => new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ M = x }}, null => null }}" + : $"new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ M = {invocation} }}"; return invocation; } From 29bf5a302f64ee8454f3b980368ef12c0a62e201 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Thu, 17 Apr 2025 22:09:52 +0200 Subject: [PATCH 03/31] Avoid specifying type when field is known --- .../Generations/AttributeExpressionName.cs | 2 +- .../Generations/AttributeExpressionValue.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs index a484a14f..66b7853e 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs @@ -40,7 +40,7 @@ private static IEnumerable TypeContent( var ternaryExpressionName = $"{ConstructorAttributeName} is null ? {@$"""#{x.DDB.AttributeName}"""}: {@$"$""{{{ConstructorAttributeName}}}.#{x.DDB.AttributeName}"""}"; return x.IsUnknown - ? $"{x.DDB.DataMember.NameAsPrivateField} = new (() => new {x.AttributeReference}({ternaryExpressionName}, {ConstructorSetName}));" + ? $"{x.DDB.DataMember.NameAsPrivateField} = new (() => new ({ternaryExpressionName}, {ConstructorSetName}));" : $"{x.DDB.DataMember.NameAsPrivateField} = new (() => {ternaryExpressionName});"; }) .Append($"{SetFieldName} = {ConstructorSetName};") diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs index 33f6d8d7..5bfcae7a 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs @@ -31,7 +31,7 @@ MarshallerOptions options const string self = "_this"; var constructorFieldAssignments = dataMembers .Select(x => x.IsUnknown - ? $"{x.DDB.DataMember.NameAsPrivateField} = new (() => new {x.AttributeReference}({ValueProvider}, {MarshallerOptions.ParamReference}));" + ? $"{x.DDB.DataMember.NameAsPrivateField} = new (() => new ({ValueProvider}, {MarshallerOptions.ParamReference}));" : $"{x.DDB.DataMember.NameAsPrivateField} = new ({ValueProvider});") .Append($"{self} = new({ValueProvider});") .Append($"{MarshallerOptions.FieldReference} = {MarshallerOptions.ParamReference};"); From 995ee60db7095fb54353749c68d49b650da8b73b Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Thu, 17 Apr 2025 22:20:20 +0200 Subject: [PATCH 04/31] Only default to AttributeValue null when the type is nullable --- .../Generations/AttributeExpressionValue.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs index 5bfcae7a..b442a076 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs @@ -100,9 +100,12 @@ private static IEnumerable YieldSelector( .ScopeTo($"if ({x.DDB.DataMember.NameAsPrivateField}.IsValueCreated)"); } + return $"if ({x.DDB.DataMember.NameAsPrivateField}.IsValueCreated)".CreateScope( - x.DDB.DataMember.Type.NotNullIfStatement(accessPattern, - $"yield return new ({x.DDB.DataMember.NameAsPrivateField}.Value, {Marshaller.InvokeMarshallerMethod(x.DDB.DataMember.Type, $"entity.{x.DDB.DataMember.Name}", $"\"{x.DDB.DataMember.Name}\"", options, MarshallerOptions.FieldReference)} ?? {AttributeValueUtilityFactory.Null});")); + x.DDB.DataMember.Type.NotNullIfStatement( + accessPattern, + $"yield return new ({x.DDB.DataMember.NameAsPrivateField}.Value, {Marshaller.InvokeMarshallerMethod(x.DDB.DataMember.Type, $"entity.{x.DDB.DataMember.Name}", $"\"{x.DDB.DataMember.Name}\"", options, MarshallerOptions.FieldReference)}{(x.DDB.DataMember.Type.IsNullable()?$" ?? {AttributeValueUtilityFactory.Null}" : null)});" + )); } internal static IEnumerable CreateClasses(DynamoDBMarshallerArguments[] arguments, From 78169598210cadd4a2cf788c8fbac7768b21f7a8 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Thu, 17 Apr 2025 22:32:42 +0200 Subject: [PATCH 05/31] Avoid null coaslescing if type system do not expect it --- .../Extensions/TypeExtensions.cs | 12 +++--------- .../Generations/AttributeExpressionValue.cs | 6 ++++-- .../Models/Address.cs | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs index ccc15b49..ad263489 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs @@ -54,20 +54,14 @@ public static Func CacheFactory(IEqualityComparer co private static readonly Dictionary TypeIdentifierDictionary = new(SymbolEqualityComparer.IncludeNullability); - public static bool IsNullable(this ITypeSymbol typeSymbol) - { - return typeSymbol switch + public static bool IsNullable(this ITypeSymbol typeSymbol) => typeSymbol switch { - { - IsReferenceType: true, NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated - } => true, + { IsReferenceType: true, NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => true, { IsReferenceType: true, NullableAnnotation: NullableAnnotation.NotAnnotated } => false, { IsValueType: true, OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => true, { IsValueType: true } => false, - _ => throw new ArgumentOutOfRangeException( - $"Could not determine nullablity of type '{typeSymbol.ToDisplayString()}'.") + _ => throw new ArgumentOutOfRangeException($"Could not determine nullablity of type '{typeSymbol.ToDisplayString()}'.") }; - } public static TypeIdentifier TypeIdentifier(this ITypeSymbol type) { diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs index b442a076..883424f6 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs @@ -72,7 +72,7 @@ _ when typeSymbol.IsNullable() => $"if ({param} is null)".CreateScope( .Concat(dataMembers .SelectMany(x => YieldSelector(x, options)) .Append( - $"if ({self}.IsValueCreated) yield return new ({self}.Value, {Marshaller.InvokeMarshallerMethod(typeSymbol, "entity", $"\"{structName}\"", options, MarshallerOptions.FieldReference)} ?? {AttributeValueUtilityFactory.Null});") + $"if ({self}.IsValueCreated) yield return new ({self}.Value, {Marshaller.InvokeMarshallerMethod(typeSymbol, "entity", $"\"{structName}\"", options, MarshallerOptions.FieldReference)}{HandeNullability(typeSymbol)});") ) .ScopeTo( $"IEnumerable> {interfaceName}.{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerAccessedValues}({typeSymbol.Representation().annotated} entity)"); @@ -84,6 +84,8 @@ _ when typeSymbol.IsNullable() => $"if ({param} is null)".CreateScope( yield return $"public override string ToString() => {self}.Value;"; } + private static string? HandeNullability(ITypeSymbol typeSymbol) => typeSymbol.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null; + private static IEnumerable YieldSelector( (bool IsUnknown, DynamoDbDataMember DDB, string AttributeReference, string AttributeInterfaceName) x, MarshallerOptions options) @@ -104,7 +106,7 @@ private static IEnumerable YieldSelector( return $"if ({x.DDB.DataMember.NameAsPrivateField}.IsValueCreated)".CreateScope( x.DDB.DataMember.Type.NotNullIfStatement( accessPattern, - $"yield return new ({x.DDB.DataMember.NameAsPrivateField}.Value, {Marshaller.InvokeMarshallerMethod(x.DDB.DataMember.Type, $"entity.{x.DDB.DataMember.Name}", $"\"{x.DDB.DataMember.Name}\"", options, MarshallerOptions.FieldReference)}{(x.DDB.DataMember.Type.IsNullable()?$" ?? {AttributeValueUtilityFactory.Null}" : null)});" + $"yield return new ({x.DDB.DataMember.NameAsPrivateField}.Value, {Marshaller.InvokeMarshallerMethod(x.DDB.DataMember.Type, $"entity.{x.DDB.DataMember.Name}", $"\"{x.DDB.DataMember.Name}\"", options, MarshallerOptions.FieldReference)}{HandeNullability(x.DDB.DataMember.Type)});" )); } diff --git a/tests/DynamoDBGenerator.SourceGenerator.Benchmarks/Models/Address.cs b/tests/DynamoDBGenerator.SourceGenerator.Benchmarks/Models/Address.cs index 97d8c91a..996e1bd2 100644 --- a/tests/DynamoDBGenerator.SourceGenerator.Benchmarks/Models/Address.cs +++ b/tests/DynamoDBGenerator.SourceGenerator.Benchmarks/Models/Address.cs @@ -4,7 +4,7 @@ public class Address { public string Id { get; set; } = null!; - public string Street { get; set; } = null!; + public string? Street { get; set; } = null!; public PostalCode PostalCode { get; set; } = null!; From 158e645a598bc68239df95addaad4afd09f58bb1 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Thu, 17 Apr 2025 22:35:15 +0200 Subject: [PATCH 06/31] Use scoped if --- .../Generations/AttributeExpressionValue.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs index 883424f6..bba4844b 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs @@ -71,8 +71,12 @@ _ when typeSymbol.IsNullable() => $"if ({param} is null)".CreateScope( }) .Concat(dataMembers .SelectMany(x => YieldSelector(x, options)) - .Append( - $"if ({self}.IsValueCreated) yield return new ({self}.Value, {Marshaller.InvokeMarshallerMethod(typeSymbol, "entity", $"\"{structName}\"", options, MarshallerOptions.FieldReference)}{HandeNullability(typeSymbol)});") + .Concat( + $"if ({self}.IsValueCreated)" + .CreateScope( + $"yield return new ({self}.Value, {Marshaller.InvokeMarshallerMethod(typeSymbol, "entity", $"\"{structName}\"", options, MarshallerOptions.FieldReference)}{HandeNullability(typeSymbol)});" + ) + ) ) .ScopeTo( $"IEnumerable> {interfaceName}.{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerAccessedValues}({typeSymbol.Representation().annotated} entity)"); @@ -84,7 +88,8 @@ _ when typeSymbol.IsNullable() => $"if ({param} is null)".CreateScope( yield return $"public override string ToString() => {self}.Value;"; } - private static string? HandeNullability(ITypeSymbol typeSymbol) => typeSymbol.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null; + private static string? HandeNullability(ITypeSymbol typeSymbol) => + typeSymbol.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null; private static IEnumerable YieldSelector( (bool IsUnknown, DynamoDbDataMember DDB, string AttributeReference, string AttributeInterfaceName) x, From 5e358956dd4cbcca81e283bdff94eda2f4343cde Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Thu, 17 Apr 2025 22:51:01 +0200 Subject: [PATCH 07/31] Use indexer when assigning --- .../Generations/Marshalling/Marshaller.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index 5bf4e16b..0b25b372 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -27,8 +27,8 @@ private static CodeFactory CreateDictionaryMethod(ITypeSymbol typeSymbol, Func Date: Thu, 17 Apr 2025 22:54:23 +0200 Subject: [PATCH 08/31] Remove redundant parantheses --- .../Generations/UnMarshaller.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index dd84020c..2fc29186 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -77,11 +77,11 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func Date: Thu, 17 Apr 2025 23:36:26 +0200 Subject: [PATCH 09/31] Avoid inline if statements --- .../Generations/AttributeExpressionName.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs index 66b7853e..d0c287d1 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs @@ -74,7 +74,7 @@ private static IEnumerable TypeContent( var yields = dataMembers .SelectMany(YieldSelector) - .Append($@"if ({self}.IsValueCreated) yield return new ({self}.Value, ""{typeSymbol.Name}"");"); + .Concat($"if ({self}.IsValueCreated)".CreateScope(@$"yield return new ({self}.Value, ""{typeSymbol.Name}"");")); foreach (var s in $"IEnumerable> {AttributeExpressionNameTrackerInterface}.{AttributeExpressionNameTrackerInterfaceAccessedNames}()" From 61a6a250a9f90f0571f64d2c0ec9a1fc7417e949 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Thu, 17 Apr 2025 23:54:45 +0200 Subject: [PATCH 10/31] Rely on scoping instead of ternaries --- .../Generations/Marshalling/Marshaller.cs | 20 +++++++++++++------ .../Generations/UnMarshaller.cs | 12 +++++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index 0b25b372..dc2846af 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -77,12 +77,15 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func type switch { { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => CreateSignature(type, options) - .CreateScope($"return {ParamReference} is not null ? {conversion} : null;") + .CreateScope($"if ({ParamReference} is null)" + .CreateScope("return null;") + .Append($"return {conversion};") + ) .ToConversion(), _ => CreateSignature(type, options) .CreateScope($"return {conversion};") @@ -90,14 +93,19 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func type switch { - { NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => CreateSignature(type, options) - .CreateScope($"return {ParamReference} is not null ? {conversion} : null;") + { NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => CreateSignature( + type, options) + .CreateScope($"if ({ParamReference} is null)".CreateScope("return null;") + .Append($"return {conversion};")) .ToConversion(), _ => CreateSignature(type, options) - .CreateScope($"return {ParamReference} is not null ? {conversion} : throw {ExceptionHelper.NullExceptionMethod}({DataMember});") + .CreateScope( + $"if ({ParamReference} is null)".CreateScope($"throw {ExceptionHelper.NullExceptionMethod}({DataMember});").Append($"return {conversion};") + ) .ToConversion() }, - _ => throw new ArgumentException($"Neither ValueType or ReferenceType could be resolved for conversion. type '{type.ToDisplayString()}'.") + _ => throw new ArgumentException( + $"Neither ValueType or ReferenceType could be resolved for conversion. type '{type.ToDisplayString()}'.") }; } diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index 2fc29186..c3e1b88a 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -77,11 +77,19 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func Date: Fri, 18 Apr 2025 09:04:20 +0200 Subject: [PATCH 11/31] Fix enum conversion --- .../Types/MarshallerOptions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs b/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs index 3ff07b36..f4d4cd2d 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs @@ -75,12 +75,12 @@ int enumStrategy var original = typeSymbol.Representation().original; return _enumStrategy switch { - ConversionStrategy.Integer => $"Int32.TryParse({attributeValueParam}.N, out var @enum) ? ({original}?)@enum : null", - ConversionStrategy.Name => $"Enum.TryParse<{original}>({attributeValueParam}.S, false, out var @enum) ? ({original}?)@enum : null", + ConversionStrategy.Integer => $"(Int32.TryParse({attributeValueParam}.N, out var e) ? ({original}?) e : null)", + ConversionStrategy.Name => $"(Enum.TryParse<{original}>({attributeValueParam}.S, false, out var e) ? ({original}?) e : null)", ConversionStrategy.NameCI or ConversionStrategy.LowerCase or ConversionStrategy.UpperCase - => $"Enum.TryParse<{original}>({attributeValueParam}.S, true, out var @enum) ? ({original}?)@enum : null", + => $"(Enum.TryParse<{original}>({attributeValueParam}.S, true, out var e) ? ({original}?) e : null)", _ => throw new ArgumentException($"Could not resolve enum conversion strategy from value '{_enumStrategy}'.") }; } From dd57db02d037cb6aa08d9aa70ff8dce959cf2170 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Fri, 18 Apr 2025 09:25:27 +0200 Subject: [PATCH 12/31] Use consistent naming scheme --- .../Extensions/TypeExtensions.cs | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs index ad263489..dd7ab126 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Immutable; +using System.Runtime.InteropServices; using DynamoDBGenerator.SourceGenerator.Types; using Microsoft.CodeAnalysis; @@ -55,13 +56,14 @@ public static Func CacheFactory(IEqualityComparer co new(SymbolEqualityComparer.IncludeNullability); public static bool IsNullable(this ITypeSymbol typeSymbol) => typeSymbol switch - { - { IsReferenceType: true, NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => true, - { IsReferenceType: true, NullableAnnotation: NullableAnnotation.NotAnnotated } => false, - { IsValueType: true, OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => true, - { IsValueType: true } => false, - _ => throw new ArgumentOutOfRangeException($"Could not determine nullablity of type '{typeSymbol.ToDisplayString()}'.") - }; + { + { IsReferenceType: true, NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => true, + { IsReferenceType: true, NullableAnnotation: NullableAnnotation.NotAnnotated } => false, + { IsValueType: true, OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => true, + { IsValueType: true } => false, + _ => throw new ArgumentOutOfRangeException( + $"Could not determine nullablity of type '{typeSymbol.ToDisplayString()}'.") + }; public static TypeIdentifier TypeIdentifier(this ITypeSymbol type) { @@ -179,6 +181,7 @@ private static int CombineHashCodes(int h1, int h2) return ((h1 << 5) + h1) ^ h2; } } + public static Func SuffixedFullyQualifiedTypeName(string suffix, IEqualityComparer comparer) { @@ -187,7 +190,7 @@ public static Func SuffixedFullyQualifiedTypeN if (Equals(comparer, SymbolEqualityComparer.Default)) { return (x, y) => dict.GetOrAdd( - (x,y), + (x, y), tuple => { var (p, c) = tuple; @@ -202,6 +205,10 @@ public static Func SuffixedFullyQualifiedTypeN ); } + private static readonly SymbolDisplayFormat DisplayFormat = new( + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters + ); + public static Func SuffixedTypeSymbolNameFactory(string? suffix, IEqualityComparer comparer) { @@ -217,8 +224,8 @@ public static Func SuffixedTypeSymbolNameFactory(string? su return x => dict.GetOrAdd(x, y => y is IArrayTypeSymbol ? $"{y.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)}_{y.BaseType!.ToDisplayString()}" - : y.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat).ToAlphaNumericMethodName() - ); + : y.ToDisplayString( + DisplayFormat).ToAlphaNumericMethodName()); return x => dict.GetOrAdd(x, y => y is IArrayTypeSymbol @@ -267,11 +274,11 @@ when string.Join("_", namedTypeSymbol.TypeArguments.Select(NullableAnnotation)) _ => throw new NotImplementedException(ExceptionMessage(single)) }, { NullableAnnotation: Microsoft.CodeAnalysis.NullableAnnotation.NotAnnotated } => - $"NN_{x.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat).ToAlphaNumericMethodName()}", + $"NN_{x.ToDisplayString(DisplayFormat).ToAlphaNumericMethodName()}", { NullableAnnotation: Microsoft.CodeAnalysis.NullableAnnotation.None } => - $"{x.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat).ToAlphaNumericMethodName()}", + $"{x.ToDisplayString(DisplayFormat).ToAlphaNumericMethodName()}", { NullableAnnotation: Microsoft.CodeAnalysis.NullableAnnotation.Annotated } => - $"N_{x.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat).ToAlphaNumericMethodName()}", + $"N_{x.ToDisplayString(DisplayFormat).ToAlphaNumericMethodName()}", _ => throw new NotImplementedException(ExceptionMessage(x)) }; From 8de6c4321b362f1dfab2e0f17523283e336bc2db Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Fri, 18 Apr 2025 09:32:58 +0200 Subject: [PATCH 13/31] Check for null --- .../Generations/Marshalling/Marshaller.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index dc2846af..5d0c2203 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -114,27 +114,27 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func singleGeneric.Type switch { SingleGeneric.SupportedType.Nullable => signature - .CreateScope($"return {ParamReference} is not null ? {InvokeMarshallerMethod(singleGeneric.T, $"{ParamReference}.Value", DataMember, options)} : null;") + .CreateScope($"return {ParamReference} is null ? null : {InvokeMarshallerMethod(singleGeneric.T, $"{ParamReference}.Value", DataMember, options)};") .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Array => signature - .CreateScope($"return {ParamReference} is not null ? {AttributeValueUtilityFactory.FromArray}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)}) : {Else(singleGeneric)};") + .CreateScope($"return {ParamReference} is null ? {IsNull(singleGeneric)} : {AttributeValueUtilityFactory.FromArray}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.List => signature - .CreateScope($"return {ParamReference} is not null ? {AttributeValueUtilityFactory.FromList}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)}) : {Else(singleGeneric)};") + .CreateScope($"return {ParamReference} is null ? {IsNull(singleGeneric)} : {AttributeValueUtilityFactory.FromList}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.IReadOnlyCollection or SingleGeneric.SupportedType.IEnumerable or SingleGeneric.SupportedType.ICollection => signature - .CreateScope($"return {ParamReference} is not null ? {AttributeValueUtilityFactory.FromEnumerable}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)}) : {Else(singleGeneric)};") + .CreateScope($"return {ParamReference} is null ? {IsNull(singleGeneric)} : {AttributeValueUtilityFactory.FromEnumerable}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set when singleGeneric.T.SpecialType is SpecialType.System_String => signature .CreateScope( - $"return {ParamReference} is not null ? new AttributeValue {{ SS = new List<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>({(singleGeneric.T.IsNullable() ? ParamReference : $"{ParamReference}.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\"))")})}} : {Else(singleGeneric)};") + $"return {ParamReference} is null ? {IsNull(singleGeneric)} : new AttributeValue {{ SS = new List<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>({(singleGeneric.T.IsNullable() ? ParamReference : $"{ParamReference}.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\"))")})}};") .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature - .CreateScope($"return {ParamReference} is not null ? new AttributeValue {{ NS = new List({ParamReference}.Select(y => y.ToString())) }} : {Else(singleGeneric)};") + .CreateScope($"return {ParamReference} is null ? {IsNull(singleGeneric)} : new AttributeValue {{ NS = new List({ParamReference}.Select(y => y.ToString())) }};") .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set => throw new ArgumentException("Only string and integers are supported for sets", UncoveredConversionException(singleGeneric, nameof(CreateMethod))), _ => throw UncoveredConversionException(singleGeneric, nameof(CreateMethod)) @@ -144,10 +144,10 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() KeyValueGeneric keyValueGeneric when CreateSignature(keyValueGeneric.TypeSymbol, options) is var signature => keyValueGeneric.Type switch { KeyValueGeneric.SupportedType.Dictionary => signature - .CreateScope($"return {ParamReference} is not null ? {AttributeValueUtilityFactory.FromDictionary}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)}) : {Else(keyValueGeneric)};") + .CreateScope($"return {ParamReference} is null ? {IsNull(keyValueGeneric)} : {AttributeValueUtilityFactory.FromDictionary}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") .ToConversion(keyValueGeneric.TValue), KeyValueGeneric.SupportedType.LookUp => signature - .CreateScope($"return {ParamReference} is not null ? {AttributeValueUtilityFactory.FromLookup}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)}) : {Else(keyValueGeneric)};") + .CreateScope($"return {ParamReference} is null ? {IsNull(keyValueGeneric)} : {AttributeValueUtilityFactory.FromLookup}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") .ToConversion(keyValueGeneric.TValue), _ => throw UncoveredConversionException(keyValueGeneric, nameof(CreateMethod)) }, @@ -164,7 +164,7 @@ private static string CreateSignature(ITypeSymbol typeSymbol, MarshallerOptions : $"public static AttributeValue {GetSerializationMethodName(typeSymbol)}({typeSymbol.Representation().annotated} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)"; } - private static string Else(TypeIdentifier typeIdentifier) + private static string IsNull(TypeIdentifier typeIdentifier) { return typeIdentifier.TypeSymbol.IsNullable() ? "null" : $"throw {ExceptionHelper.NullExceptionMethod}({DataMember})"; } From 51f473d99b061dff328197c23d25a3c1691afd5b Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Fri, 18 Apr 2025 10:00:35 +0200 Subject: [PATCH 14/31] Avoid ternaries --- .../Generations/Marshalling/Marshaller.cs | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index 5d0c2203..25ddf2a9 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -114,27 +114,46 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func singleGeneric.Type switch { SingleGeneric.SupportedType.Nullable => signature - .CreateScope($"return {ParamReference} is null ? null : {InvokeMarshallerMethod(singleGeneric.T, $"{ParamReference}.Value", DataMember, options)};") - .ToConversion(singleGeneric.T), + .CreateScope( + $"if ({ParamReference} is null)" + .CreateScope("return null;") + .Append($"return {InvokeMarshallerMethod(singleGeneric.T, $"{ParamReference}.Value", DataMember, options)};") + ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Array => signature - .CreateScope($"return {ParamReference} is null ? {IsNull(singleGeneric)} : {AttributeValueUtilityFactory.FromArray}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") - .ToConversion(singleGeneric.T), + .CreateScope( + $"if ({ParamReference} is null)" + .CreateScope($"return {IsNull(singleGeneric)};") + .Append($"return {AttributeValueUtilityFactory.FromArray}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.List => signature - .CreateScope($"return {ParamReference} is null ? {IsNull(singleGeneric)} : {AttributeValueUtilityFactory.FromList}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") - .ToConversion(singleGeneric.T), + .CreateScope( + $"if ({ParamReference} is null)" + .CreateScope($"return {IsNull(singleGeneric)};") + .Append($"return {AttributeValueUtilityFactory.FromList}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.IReadOnlyCollection or SingleGeneric.SupportedType.IEnumerable or SingleGeneric.SupportedType.ICollection => signature - .CreateScope($"return {ParamReference} is null ? {IsNull(singleGeneric)} : {AttributeValueUtilityFactory.FromEnumerable}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") - .ToConversion(singleGeneric.T), + .CreateScope( + $"if ({ParamReference} is null)" + .CreateScope($"return {IsNull(singleGeneric)};") + .Append($"return {AttributeValueUtilityFactory.FromEnumerable}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set when singleGeneric.T.SpecialType is SpecialType.System_String => signature .CreateScope( - $"return {ParamReference} is null ? {IsNull(singleGeneric)} : new AttributeValue {{ SS = new List<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>({(singleGeneric.T.IsNullable() ? ParamReference : $"{ParamReference}.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\"))")})}};") + $"if ({ParamReference} is null)" + .CreateScope($"return {IsNull(singleGeneric)};") + .Append($"return new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ SS = new List<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>({(singleGeneric.T.IsNullable() ? ParamReference : $"{ParamReference}.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\"))")})}};") + ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature - .CreateScope($"return {ParamReference} is null ? {IsNull(singleGeneric)} : new AttributeValue {{ NS = new List({ParamReference}.Select(y => y.ToString())) }};") + .CreateScope( + $"if ({ParamReference} is null)" + .CreateScope($"return {IsNull(singleGeneric)};") + .Append($"return new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ NS = new List({ParamReference}.Select(y => y.ToString())) }};") + ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set => throw new ArgumentException("Only string and integers are supported for sets", UncoveredConversionException(singleGeneric, nameof(CreateMethod))), _ => throw UncoveredConversionException(singleGeneric, nameof(CreateMethod)) @@ -144,10 +163,18 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() KeyValueGeneric keyValueGeneric when CreateSignature(keyValueGeneric.TypeSymbol, options) is var signature => keyValueGeneric.Type switch { KeyValueGeneric.SupportedType.Dictionary => signature - .CreateScope($"return {ParamReference} is null ? {IsNull(keyValueGeneric)} : {AttributeValueUtilityFactory.FromDictionary}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + .CreateScope( + $"if ({ParamReference} is null)" + .CreateScope($"return {IsNull(keyValueGeneric)};") + .Append($"return {AttributeValueUtilityFactory.FromDictionary}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + ) .ToConversion(keyValueGeneric.TValue), KeyValueGeneric.SupportedType.LookUp => signature - .CreateScope($"return {ParamReference} is null ? {IsNull(keyValueGeneric)} : {AttributeValueUtilityFactory.FromLookup}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + .CreateScope( + $"if ({ParamReference} is null)" + .CreateScope($"return {IsNull(keyValueGeneric)};") + .Append($"return {AttributeValueUtilityFactory.FromLookup}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + ) .ToConversion(keyValueGeneric.TValue), _ => throw UncoveredConversionException(keyValueGeneric, nameof(CreateMethod)) }, @@ -160,8 +187,8 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() private static string CreateSignature(ITypeSymbol typeSymbol, MarshallerOptions options) { return typeSymbol.IsNullable() - ? $"public static AttributeValue? {GetSerializationMethodName(typeSymbol)}({typeSymbol.Representation().annotated} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)" - : $"public static AttributeValue {GetSerializationMethodName(typeSymbol)}({typeSymbol.Representation().annotated} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)"; + ? $"public static {Constants.AWSSDK_DynamoDBv2.AttributeValue}? {GetSerializationMethodName(typeSymbol)}({typeSymbol.Representation().annotated} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)" + : $"public static {Constants.AWSSDK_DynamoDBv2.AttributeValue} {GetSerializationMethodName(typeSymbol)}({typeSymbol.Representation().annotated} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)"; } private static string IsNull(TypeIdentifier typeIdentifier) @@ -173,12 +200,12 @@ private static IEnumerable InitializeDictionary(IEnumerable capa var capacityCalculation = string.Join(" + ", capacityCalculations); if (capacityCalculation is "") { - yield return $"var {DictionaryReference} = new Dictionary(0);"; + yield return $"var {DictionaryReference} = new Dictionary(0);"; } else { yield return $"var capacity = {capacityCalculation};"; - yield return $"var {DictionaryReference} = new Dictionary(capacity);"; + yield return $"var {DictionaryReference} = new Dictionary(capacity);"; } } From 29ea68367a2c1ab1d6ab7c88273359111aee9e0a Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Fri, 18 Apr 2025 10:05:31 +0200 Subject: [PATCH 15/31] Fix build error --- .../Generations/Marshalling/Marshaller.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index 25ddf2a9..2c7ad90c 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -122,13 +122,13 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope($"return {IsNull(singleGeneric)};") + .CreateScope(IsNull(singleGeneric)) .Append($"return {AttributeValueUtilityFactory.FromArray}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.List => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope($"return {IsNull(singleGeneric)};") + .CreateScope(IsNull(singleGeneric)) .Append($"return {AttributeValueUtilityFactory.FromList}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.IReadOnlyCollection @@ -136,14 +136,14 @@ or SingleGeneric.SupportedType.IEnumerable or SingleGeneric.SupportedType.ICollection => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope($"return {IsNull(singleGeneric)};") + .CreateScope(IsNull(singleGeneric)) .Append($"return {AttributeValueUtilityFactory.FromEnumerable}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set when singleGeneric.T.SpecialType is SpecialType.System_String => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope($"return {IsNull(singleGeneric)};") + .CreateScope(IsNull(singleGeneric)) .Append($"return new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ SS = new List<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>({(singleGeneric.T.IsNullable() ? ParamReference : $"{ParamReference}.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\"))")})}};") ) .ToConversion(singleGeneric.T), @@ -151,7 +151,7 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope($"return {IsNull(singleGeneric)};") + .CreateScope(IsNull(singleGeneric)) .Append($"return new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ NS = new List({ParamReference}.Select(y => y.ToString())) }};") ) .ToConversion(singleGeneric.T), @@ -165,14 +165,14 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() KeyValueGeneric.SupportedType.Dictionary => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope($"return {IsNull(keyValueGeneric)};") + .CreateScope($"{IsNull(keyValueGeneric)};") .Append($"return {AttributeValueUtilityFactory.FromDictionary}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ) .ToConversion(keyValueGeneric.TValue), KeyValueGeneric.SupportedType.LookUp => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope($"return {IsNull(keyValueGeneric)};") + .CreateScope($"{IsNull(keyValueGeneric)};") .Append($"return {AttributeValueUtilityFactory.FromLookup}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ) .ToConversion(keyValueGeneric.TValue), @@ -193,7 +193,9 @@ private static string CreateSignature(ITypeSymbol typeSymbol, MarshallerOptions private static string IsNull(TypeIdentifier typeIdentifier) { - return typeIdentifier.TypeSymbol.IsNullable() ? "null" : $"throw {ExceptionHelper.NullExceptionMethod}({DataMember})"; + return typeIdentifier.TypeSymbol.IsNullable() + ? "return null;" + : $"throw {ExceptionHelper.NullExceptionMethod}({DataMember});"; } private static IEnumerable InitializeDictionary(IEnumerable capacityCalculations) { From 6fa43e465526ec1507da3b40f76300b79ca7b069 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Fri, 18 Apr 2025 10:07:09 +0200 Subject: [PATCH 16/31] Reuse IsNUll --- .../Generations/Marshalling/Marshaller.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index 2c7ad90c..f1deb2a7 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -116,7 +116,7 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope("return null;") + .CreateScope(IsNull(singleGeneric)) .Append($"return {InvokeMarshallerMethod(singleGeneric.T, $"{ParamReference}.Value", DataMember, options)};") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Array => signature From 735969f474e03da35b7d11a98fb933c5396f91d5 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Fri, 18 Apr 2025 16:07:39 +0200 Subject: [PATCH 17/31] Rely on checking for error first when unmarshalling as well --- .../Generations/UnMarshaller.cs | 56 ++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index c3e1b88a..d025cf56 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -98,24 +98,46 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func singleGeneric.Type switch { SingleGeneric.SupportedType.Nullable => signature - .CreateScope($"return {Value} is not null and {{ NULL: false }} ? {InvokeUnmarshallMethod(singleGeneric.T, Value, DataMember, options)} : null;") + .CreateScope( + $"if ({Value} is null || {Value}.NULL is true)" + .CreateScope(IsNull(singleGeneric.T)) + .Append($"return {InvokeUnmarshallMethod(singleGeneric.T, Value, DataMember, options)};" ) + ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.List or SingleGeneric.SupportedType.ICollection => signature - .CreateScope($"return {Value} is {{ L: {{ }} x }} ? {AttributeValueUtilityFactory.ToList}(x, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")}) : {Else(singleGeneric.TypeSymbol)};") + .CreateScope( + $"if ({Value} is null || {Value}.L is null)" + .CreateScope(IsNull(singleGeneric.T)) + .Append($"{AttributeValueUtilityFactory.ToList}({Value}.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")})") + ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Array or SingleGeneric.SupportedType.IReadOnlyCollection => signature - .CreateScope($"return {Value} is {{ L: {{ }} x }} ? {AttributeValueUtilityFactory.ToArray}(x, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")}) : {Else(singleGeneric.TypeSymbol)};") + .CreateScope( + $"if ({Value} is null || {Value}.L is null)" + .CreateScope(IsNull(singleGeneric.T)) + .Append($"return {AttributeValueUtilityFactory.ToArray}(x.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") + ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.IEnumerable => signature - .CreateScope($"return {Value} is {{ L: {{ }} x }} ? {AttributeValueUtilityFactory.ToEnumerable}(x, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")}) : {Else(singleGeneric.TypeSymbol)};") + .CreateScope( + $"if ({Value} is null || {Value}.L is null)" + .CreateScope(IsNull(singleGeneric.T)) + .Append($"return {AttributeValueUtilityFactory.ToEnumerable}(x.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") + ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set when singleGeneric.T.SpecialType is SpecialType.System_String => signature .CreateScope( - $"return {Value} is {{ SS : {{ }} x }} ? new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>" : null)}({(singleGeneric.T.IsNullable() ? "x" : $"x.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\")")})) : {Else(singleGeneric.TypeSymbol)};") + $"if ({Value} is null || {Value}.SS is null)" + .CreateScope(IsNull(singleGeneric.T)) + .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>" : null)}({(singleGeneric.T.IsNullable() ? $"{Value}.SS" : $"{Value}.SS.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\")")}));") + ) .ToConversion(), SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature .CreateScope( - $"return {Value} is {{ NS : {{ }} x }} ? new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{singleGeneric.T.Representation().original}>" : null)}(x.Select(y => {singleGeneric.T.Representation().original}.Parse(y))) : {Else(singleGeneric.TypeSymbol)};") + $"if ({Value} is null || {Value}.NS is null)" + .CreateScope(IsNull(singleGeneric.T)) + .Append($"new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{singleGeneric.T.Representation().original}>" : null)}({Value}.NS.Select(y => {singleGeneric.T.Representation().original}.Parse(y)))") + ) .ToConversion(singleGeneric.TypeSymbol), SingleGeneric.SupportedType.Set => throw new ArgumentException("Only string and integers are supported for sets", UncoveredConversionException(singleGeneric, nameof(CreateMethod))), _ => throw UncoveredConversionException(singleGeneric, nameof(CreateMethod)) @@ -125,10 +147,18 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature KeyValueGeneric keyValueGeneric when CreateSignature(keyValueGeneric.TypeSymbol, options) is var signature => keyValueGeneric.Type switch { KeyValueGeneric.SupportedType.Dictionary => signature - .CreateScope($"return {Value} is {{ M: {{ }} x }} ? {AttributeValueUtilityFactory.ToDictionary}(x, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(keyValueGeneric.TValue, "a", "d", options, "o")}) : {Else(keyValueGeneric.TypeSymbol)};") + .CreateScope( + $"if ({Value} is null || {Value}.M is null)" + .CreateScope(IsNull(keyValueGeneric.TypeSymbol)) + .Append($"return {AttributeValueUtilityFactory.ToDictionary}({Value}.M, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(keyValueGeneric.TValue, "a", "d", options, "o")});") + ) .ToConversion(keyValueGeneric.TValue), KeyValueGeneric.SupportedType.LookUp => signature - .CreateScope($"return {Value} is {{ M: {{ }} x }} ? {AttributeValueUtilityFactory.ToLookup}(x, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(keyValueGeneric.TValue, "a", "d", options, "o")}) : {Else(keyValueGeneric.TypeSymbol)};") + .CreateScope( + $"if ({Value} is null || {Value}.M is null)" + .CreateScope(IsNull(keyValueGeneric.TypeSymbol)) + .Append($"return {AttributeValueUtilityFactory.ToLookup}({Value}.M, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(keyValueGeneric.TValue, "a", "d", options, "o")});") + ) .ToConversion(keyValueGeneric.TValue), _ => throw UncoveredConversionException(keyValueGeneric, nameof(CreateMethod)) @@ -155,11 +185,7 @@ private static IEnumerable CreateTypeContents(IEnumerable ObjectAssignmentBlock(bool useParentheses, IE } + private static string IsNull(ITypeSymbol typeSymbol) => typeSymbol.IsNullable() + ? "return null;" + : $"throw {ExceptionHelper.NullExceptionMethod}({DataMember});"; + internal static IEnumerable RootSignature(ITypeSymbol typeSymbol, string rootTypeName) { return $"public {rootTypeName} {Marshaller.UnmarshalMethodName}(Dictionary<{nameof(String)}, {Constants.AWSSDK_DynamoDBv2.AttributeValue}> entity)".CreateScope( From f106960d5268f07ec947fed444b86c03046f731c Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Fri, 18 Apr 2025 16:18:44 +0200 Subject: [PATCH 18/31] Reuse IsNullLogic --- .../Extensions/TypeExtensions.cs | 4 +++ .../Generations/Marshalling/Marshaller.cs | 22 ++++++--------- .../Generations/UnMarshaller.cs | 28 +++++++++---------- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs index dd7ab126..6eb200d0 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs @@ -55,6 +55,10 @@ public static Func CacheFactory(IEqualityComparer co private static readonly Dictionary TypeIdentifierDictionary = new(SymbolEqualityComparer.IncludeNullability); + public static string ReturnNullOrThrow(this ITypeSymbol typeSymbol, string dataMember) => typeSymbol.IsNullable() + ? "return null;" + : $"throw {Constants.DynamoDBGenerator.ExceptionHelper.NullExceptionMethod}({dataMember});"; + public static bool IsNullable(this ITypeSymbol typeSymbol) => typeSymbol switch { { IsReferenceType: true, NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => true, diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index f1deb2a7..09a9a48d 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -116,19 +116,19 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(IsNull(singleGeneric)) + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {InvokeMarshallerMethod(singleGeneric.T, $"{ParamReference}.Value", DataMember, options)};") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Array => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(IsNull(singleGeneric)) + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.FromArray}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.List => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(IsNull(singleGeneric)) + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.FromList}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.IReadOnlyCollection @@ -136,14 +136,14 @@ or SingleGeneric.SupportedType.IEnumerable or SingleGeneric.SupportedType.ICollection => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(IsNull(singleGeneric)) + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.FromEnumerable}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set when singleGeneric.T.SpecialType is SpecialType.System_String => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(IsNull(singleGeneric)) + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ SS = new List<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>({(singleGeneric.T.IsNullable() ? ParamReference : $"{ParamReference}.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\"))")})}};") ) .ToConversion(singleGeneric.T), @@ -151,7 +151,7 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(IsNull(singleGeneric)) + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ NS = new List({ParamReference}.Select(y => y.ToString())) }};") ) .ToConversion(singleGeneric.T), @@ -165,14 +165,14 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() KeyValueGeneric.SupportedType.Dictionary => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope($"{IsNull(keyValueGeneric)};") + .CreateScope(keyValueGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.FromDictionary}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ) .ToConversion(keyValueGeneric.TValue), KeyValueGeneric.SupportedType.LookUp => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope($"{IsNull(keyValueGeneric)};") + .CreateScope(keyValueGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.FromLookup}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ) .ToConversion(keyValueGeneric.TValue), @@ -191,12 +191,6 @@ private static string CreateSignature(ITypeSymbol typeSymbol, MarshallerOptions : $"public static {Constants.AWSSDK_DynamoDBv2.AttributeValue} {GetSerializationMethodName(typeSymbol)}({typeSymbol.Representation().annotated} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)"; } - private static string IsNull(TypeIdentifier typeIdentifier) - { - return typeIdentifier.TypeSymbol.IsNullable() - ? "return null;" - : $"throw {ExceptionHelper.NullExceptionMethod}({DataMember});"; - } private static IEnumerable InitializeDictionary(IEnumerable capacityCalculations) { var capacityCalculation = string.Join(" + ", capacityCalculations); diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index d025cf56..4c8bd657 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -100,43 +100,43 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func signature .CreateScope( $"if ({Value} is null || {Value}.NULL is true)" - .CreateScope(IsNull(singleGeneric.T)) + .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) .Append($"return {InvokeUnmarshallMethod(singleGeneric.T, Value, DataMember, options)};" ) ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.List or SingleGeneric.SupportedType.ICollection => signature .CreateScope( $"if ({Value} is null || {Value}.L is null)" - .CreateScope(IsNull(singleGeneric.T)) - .Append($"{AttributeValueUtilityFactory.ToList}({Value}.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")})") + .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) + .Append($"return {AttributeValueUtilityFactory.ToList}({Value}.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Array or SingleGeneric.SupportedType.IReadOnlyCollection => signature .CreateScope( $"if ({Value} is null || {Value}.L is null)" - .CreateScope(IsNull(singleGeneric.T)) - .Append($"return {AttributeValueUtilityFactory.ToArray}(x.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") + .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) + .Append($"return {AttributeValueUtilityFactory.ToArray}({Value}.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.IEnumerable => signature .CreateScope( $"if ({Value} is null || {Value}.L is null)" - .CreateScope(IsNull(singleGeneric.T)) - .Append($"return {AttributeValueUtilityFactory.ToEnumerable}(x.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") + .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) + .Append($"return {AttributeValueUtilityFactory.ToEnumerable}({Value}.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set when singleGeneric.T.SpecialType is SpecialType.System_String => signature .CreateScope( $"if ({Value} is null || {Value}.SS is null)" - .CreateScope(IsNull(singleGeneric.T)) + .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>" : null)}({(singleGeneric.T.IsNullable() ? $"{Value}.SS" : $"{Value}.SS.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\")")}));") ) .ToConversion(), SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature .CreateScope( $"if ({Value} is null || {Value}.NS is null)" - .CreateScope(IsNull(singleGeneric.T)) - .Append($"new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{singleGeneric.T.Representation().original}>" : null)}({Value}.NS.Select(y => {singleGeneric.T.Representation().original}.Parse(y)))") + .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) + .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{singleGeneric.T.Representation().original}>" : null)}({Value}.NS.Select(y => {singleGeneric.T.Representation().original}.Parse(y)))") ) .ToConversion(singleGeneric.TypeSymbol), SingleGeneric.SupportedType.Set => throw new ArgumentException("Only string and integers are supported for sets", UncoveredConversionException(singleGeneric, nameof(CreateMethod))), @@ -149,14 +149,14 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature KeyValueGeneric.SupportedType.Dictionary => signature .CreateScope( $"if ({Value} is null || {Value}.M is null)" - .CreateScope(IsNull(keyValueGeneric.TypeSymbol)) + .CreateScope(keyValueGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.ToDictionary}({Value}.M, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(keyValueGeneric.TValue, "a", "d", options, "o")});") ) .ToConversion(keyValueGeneric.TValue), KeyValueGeneric.SupportedType.LookUp => signature .CreateScope( $"if ({Value} is null || {Value}.M is null)" - .CreateScope(IsNull(keyValueGeneric.TypeSymbol)) + .CreateScope(keyValueGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.ToLookup}({Value}.M, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(keyValueGeneric.TValue, "a", "d", options, "o")});") ) .ToConversion(keyValueGeneric.TValue), @@ -173,6 +173,7 @@ private static string CreateSignature(ITypeSymbol typeSymbol, MarshallerOptions { return $"public static {typeSymbol.Representation().annotated} {GetDeserializationMethodName(typeSymbol)}(AttributeValue? {Value}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)"; } + private static IEnumerable CreateTypeContents(IEnumerable arguments, Func getDynamoDbProperties, MarshallerOptions options) { @@ -226,9 +227,6 @@ private static IEnumerable ObjectAssignmentBlock(bool useParentheses, IE } - private static string IsNull(ITypeSymbol typeSymbol) => typeSymbol.IsNullable() - ? "return null;" - : $"throw {ExceptionHelper.NullExceptionMethod}({DataMember});"; internal static IEnumerable RootSignature(ITypeSymbol typeSymbol, string rootTypeName) { From 0ae3a50ca2aabcd1033d00088fc2229c0dbab52d Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Fri, 18 Apr 2025 16:22:25 +0200 Subject: [PATCH 19/31] Fix build errors --- .../Generations/UnMarshaller.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index 4c8bd657..4d8ff339 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -100,43 +100,43 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func signature .CreateScope( $"if ({Value} is null || {Value}.NULL is true)" - .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {InvokeUnmarshallMethod(singleGeneric.T, Value, DataMember, options)};" ) ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.List or SingleGeneric.SupportedType.ICollection => signature .CreateScope( $"if ({Value} is null || {Value}.L is null)" - .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.ToList}({Value}.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Array or SingleGeneric.SupportedType.IReadOnlyCollection => signature .CreateScope( $"if ({Value} is null || {Value}.L is null)" - .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.ToArray}({Value}.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.IEnumerable => signature .CreateScope( $"if ({Value} is null || {Value}.L is null)" - .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.ToEnumerable}({Value}.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set when singleGeneric.T.SpecialType is SpecialType.System_String => signature .CreateScope( $"if ({Value} is null || {Value}.SS is null)" - .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>" : null)}({(singleGeneric.T.IsNullable() ? $"{Value}.SS" : $"{Value}.SS.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\")")}));") ) .ToConversion(), SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature .CreateScope( $"if ({Value} is null || {Value}.NS is null)" - .CreateScope(singleGeneric.T.ReturnNullOrThrow(DataMember)) - .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{singleGeneric.T.Representation().original}>" : null)}({Value}.NS.Select(y => {singleGeneric.T.Representation().original}.Parse(y)))") + .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{singleGeneric.T.Representation().original}>" : null)}({Value}.NS.Select(y => {singleGeneric.T.Representation().original}.Parse(y)));") ) .ToConversion(singleGeneric.TypeSymbol), SingleGeneric.SupportedType.Set => throw new ArgumentException("Only string and integers are supported for sets", UncoveredConversionException(singleGeneric, nameof(CreateMethod))), From 7c7ab7d0e3896de46de044642837a55a86f887b7 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Fri, 18 Apr 2025 16:28:15 +0200 Subject: [PATCH 20/31] Increase functionality of TypeIdentifier --- .../Extensions/TypeExtensions.cs | 4 -- .../Generations/Marshalling/Marshaller.cs | 16 +++--- .../Generations/UnMarshaller.cs | 16 +++--- .../Types/TypeIdentifier.cs | 55 +++++++++++++------ 4 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs index 6eb200d0..dd7ab126 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs @@ -55,10 +55,6 @@ public static Func CacheFactory(IEqualityComparer co private static readonly Dictionary TypeIdentifierDictionary = new(SymbolEqualityComparer.IncludeNullability); - public static string ReturnNullOrThrow(this ITypeSymbol typeSymbol, string dataMember) => typeSymbol.IsNullable() - ? "return null;" - : $"throw {Constants.DynamoDBGenerator.ExceptionHelper.NullExceptionMethod}({dataMember});"; - public static bool IsNullable(this ITypeSymbol typeSymbol) => typeSymbol switch { { IsReferenceType: true, NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => true, diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index 09a9a48d..edcd0ac2 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -116,19 +116,19 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {InvokeMarshallerMethod(singleGeneric.T, $"{ParamReference}.Value", DataMember, options)};") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Array => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.FromArray}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.List => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.FromList}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.IReadOnlyCollection @@ -136,14 +136,14 @@ or SingleGeneric.SupportedType.IEnumerable or SingleGeneric.SupportedType.ICollection => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.FromEnumerable}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set when singleGeneric.T.SpecialType is SpecialType.System_String => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ SS = new List<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>({(singleGeneric.T.IsNullable() ? ParamReference : $"{ParamReference}.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\"))")})}};") ) .ToConversion(singleGeneric.T), @@ -151,7 +151,7 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ NS = new List({ParamReference}.Select(y => y.ToString())) }};") ) .ToConversion(singleGeneric.T), @@ -165,14 +165,14 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() KeyValueGeneric.SupportedType.Dictionary => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(keyValueGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(keyValueGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.FromDictionary}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ) .ToConversion(keyValueGeneric.TValue), KeyValueGeneric.SupportedType.LookUp => signature .CreateScope( $"if ({ParamReference} is null)" - .CreateScope(keyValueGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(keyValueGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.FromLookup}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ) .ToConversion(keyValueGeneric.TValue), diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index 4d8ff339..b9b28ada 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -100,42 +100,42 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func signature .CreateScope( $"if ({Value} is null || {Value}.NULL is true)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {InvokeUnmarshallMethod(singleGeneric.T, Value, DataMember, options)};" ) ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.List or SingleGeneric.SupportedType.ICollection => signature .CreateScope( $"if ({Value} is null || {Value}.L is null)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.ToList}({Value}.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Array or SingleGeneric.SupportedType.IReadOnlyCollection => signature .CreateScope( $"if ({Value} is null || {Value}.L is null)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.ToArray}({Value}.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.IEnumerable => signature .CreateScope( $"if ({Value} is null || {Value}.L is null)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.ToEnumerable}({Value}.L, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") ) .ToConversion(singleGeneric.T), SingleGeneric.SupportedType.Set when singleGeneric.T.SpecialType is SpecialType.System_String => signature .CreateScope( $"if ({Value} is null || {Value}.SS is null)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>" : null)}({(singleGeneric.T.IsNullable() ? $"{Value}.SS" : $"{Value}.SS.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\")")}));") ) .ToConversion(), SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature .CreateScope( $"if ({Value} is null || {Value}.NS is null)" - .CreateScope(singleGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{singleGeneric.T.Representation().original}>" : null)}({Value}.NS.Select(y => {singleGeneric.T.Representation().original}.Parse(y)));") ) .ToConversion(singleGeneric.TypeSymbol), @@ -149,14 +149,14 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature KeyValueGeneric.SupportedType.Dictionary => signature .CreateScope( $"if ({Value} is null || {Value}.M is null)" - .CreateScope(keyValueGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(keyValueGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.ToDictionary}({Value}.M, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(keyValueGeneric.TValue, "a", "d", options, "o")});") ) .ToConversion(keyValueGeneric.TValue), KeyValueGeneric.SupportedType.LookUp => signature .CreateScope( $"if ({Value} is null || {Value}.M is null)" - .CreateScope(keyValueGeneric.TypeSymbol.ReturnNullOrThrow(DataMember)) + .CreateScope(keyValueGeneric.ReturnNullOrThrow(DataMember)) .Append($"return {AttributeValueUtilityFactory.ToLookup}({Value}.M, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeUnmarshallMethod(keyValueGeneric.TValue, "a", "d", options, "o")});") ) .ToConversion(keyValueGeneric.TValue), diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs b/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs index 2d5004df..f1ed9dd5 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs @@ -1,9 +1,19 @@ using DynamoDBGenerator.SourceGenerator.Extensions; using Microsoft.CodeAnalysis; + namespace DynamoDBGenerator.SourceGenerator.Types; public abstract record TypeIdentifier(ITypeSymbol TypeSymbol) { + private bool IsNullable { get; } = TypeSymbol.IsNullable(); + + public string ReturnNullOrThrow(string dataMember) + { + return IsNullable + ? "return null;" + : $"throw {Constants.DynamoDBGenerator.ExceptionHelper.NullExceptionMethod}({dataMember});"; + } + public ITypeSymbol TypeSymbol { get; } = TypeSymbol; } @@ -11,19 +21,20 @@ public sealed record UnknownType(ITypeSymbol TypeSymbol) : TypeIdentifier(TypeSy public sealed record KeyValueGeneric : TypeIdentifier { - public enum SupportedType { LookUp = 1, Dictionary = 2 } - private KeyValueGeneric(in ITypeSymbol typeSymbol, in ITypeSymbol tKey, in ITypeSymbol tValue, in SupportedType supportedType) : base(typeSymbol) + private KeyValueGeneric(in ITypeSymbol typeSymbol, in ITypeSymbol tKey, in ITypeSymbol tValue, + in SupportedType supportedType) : base(typeSymbol) { Type = supportedType; TKey = tKey; TValue = tValue; } + public SupportedType Type { get; } // ReSharper disable once InconsistentNaming @@ -37,13 +48,13 @@ private KeyValueGeneric(in ITypeSymbol typeSymbol, in ITypeSymbol tKey, in IType if (typeSymbol is not INamedTypeSymbol type) return null; - if (type is not {IsGenericType: true, TypeArguments.Length: 2}) + if (type is not { IsGenericType: true, TypeArguments.Length: 2 }) return null; SupportedType? supported = type switch { - {Name: "ILookup"} => SupportedType.LookUp, - {Name: "Dictionary" or "IReadOnlyDictionary" or "IDictionary"} => SupportedType.Dictionary, + { Name: "ILookup" } => SupportedType.LookUp, + { Name: "Dictionary" or "IReadOnlyDictionary" or "IDictionary" } => SupportedType.Dictionary, _ => null }; return supported is null @@ -54,7 +65,6 @@ private KeyValueGeneric(in ITypeSymbol typeSymbol, in ITypeSymbol tKey, in IType public sealed record SingleGeneric : TypeIdentifier { - public enum SupportedType { Nullable = 1, @@ -72,6 +82,7 @@ private SingleGeneric(ITypeSymbol type, ITypeSymbol innerType, in SupportedType Type = supportedType; T = innerType; } + public SupportedType Type { get; } public ITypeSymbol T { get; } @@ -84,25 +95,33 @@ private SingleGeneric(ITypeSymbol type, ITypeSymbol innerType, in SupportedType if (typeSymbol is not INamedTypeSymbol type) return null; - if (type is not {IsGenericType: true, TypeArguments.Length: 1}) + if (type is not { IsGenericType: true, TypeArguments.Length: 1 }) return null; SupportedType? supported = type switch { _ when type.TryGetNullableValueType() is not null => SupportedType.Nullable, - _ when type.OriginalDefinition.ToDisplayString() == "System.Collections.Generic.List" => SupportedType.List, - {Name: "ISet" } => SupportedType.Set, - _ when type.AllInterfaces.Any(x => x is {Name: "ISet"}) => SupportedType.Set, - {Name: "IReadOnlySet" } => SupportedType.Set, - _ when type.AllInterfaces.Any(x => x is {Name: "IReadOnlySet"}) => SupportedType.Set, - {OriginalDefinition.SpecialType: SpecialType.System_Collections_Generic_ICollection_T} => SupportedType.ICollection, - _ when type.AllInterfaces.Any(x => x is {OriginalDefinition.SpecialType: SpecialType.System_Collections_Generic_ICollection_T}) => SupportedType.ICollection, - {OriginalDefinition.SpecialType: SpecialType.System_Collections_Generic_IReadOnlyCollection_T} => SupportedType.IReadOnlyCollection, - _ when type.AllInterfaces.Any(x => x is {OriginalDefinition.SpecialType: SpecialType.System_Collections_Generic_IReadOnlyCollection_T}) => SupportedType.IReadOnlyCollection, - {OriginalDefinition.SpecialType: SpecialType.System_Collections_Generic_IEnumerable_T} => SupportedType.IEnumerable, + _ when type.OriginalDefinition.ToDisplayString() == "System.Collections.Generic.List" => SupportedType + .List, + { Name: "ISet" } => SupportedType.Set, + _ when type.AllInterfaces.Any(x => x is { Name: "ISet" }) => SupportedType.Set, + { Name: "IReadOnlySet" } => SupportedType.Set, + _ when type.AllInterfaces.Any(x => x is { Name: "IReadOnlySet" }) => SupportedType.Set, + { OriginalDefinition.SpecialType: SpecialType.System_Collections_Generic_ICollection_T } => SupportedType + .ICollection, + _ when type.AllInterfaces.Any(x => x is + { OriginalDefinition.SpecialType: SpecialType.System_Collections_Generic_ICollection_T }) => + SupportedType.ICollection, + { OriginalDefinition.SpecialType: SpecialType.System_Collections_Generic_IReadOnlyCollection_T } => + SupportedType.IReadOnlyCollection, + _ when type.AllInterfaces.Any(x => x is + { OriginalDefinition.SpecialType: SpecialType.System_Collections_Generic_IReadOnlyCollection_T }) => + SupportedType.IReadOnlyCollection, + { OriginalDefinition.SpecialType: SpecialType.System_Collections_Generic_IEnumerable_T } => SupportedType + .IEnumerable, _ => null }; return supported is null ? null : new SingleGeneric(type, type.TypeArguments[0], supported.Value); } -} +} \ No newline at end of file From 421abfbc0c801e216475d67b569f94e69b457571 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Sat, 19 Apr 2025 09:42:51 +0200 Subject: [PATCH 21/31] Stop relying on special syntax --- .../Generations/UnMarshaller.cs | 65 +++++++++++++++---- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index b9b28ada..aa193718 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -75,22 +75,59 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func type switch + { + { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => CreateSignature(type, options) + .CreateScope( + $"if ({Value} is null)" + .CreateScope("return null;") + .Append($"return {conversion}.Value;") + ) + .ToConversion(), + _ => CreateSignature(type, options) + .CreateScope( + $"if ({Value} is null)" + .CreateScope($"throw {ExceptionHelper.NullExceptionMethod}({DataMember});") + .Append($"var result = {conversion};") + .Concat( + "if (result is null)" + .CreateScope($"throw {ExceptionHelper.NullExceptionMethod}({DataMember});") + ) + .Append("return result.Value;") + ) + .ToConversion() + + }, + { IsReferenceType: true } => type switch + { + { NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => CreateSignature( + type, options) + .CreateScope( + $"if ({Value} is null)" + .CreateScope("return null;") + .Append($"return {conversion};") + ).ToConversion(), + _ => CreateSignature(type, options) + .CreateScope( + $"if ({Value} is null)" + .CreateScope($"throw {ExceptionHelper.NullExceptionMethod}({DataMember});") + .Append($"var result = {conversion};") + .Concat( + "if (result is null)" + .CreateScope($"throw {ExceptionHelper.NullExceptionMethod}({DataMember});") + ) + .Append("return result;") + ) + .ToConversion() + - return CreateSignature(type, options) - .CreateScope( - $"if ({Value} is null || {conversion} is not {{ }} x)" - .CreateScope($"throw {ExceptionHelper.NullExceptionMethod}({DataMember});") - .Append("return x;") + }, + _ => throw new ArgumentException( + $"Neither ValueType or ReferenceType could be resolved for conversion. type '{type.ToDisplayString()}'." ) - .ToConversion(); + }; } return type.TypeIdentifier() switch From 6d6f7e0f895a563573b1af608d92339032a4df39 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Sat, 19 Apr 2025 09:54:40 +0200 Subject: [PATCH 22/31] Create variables --- .../Generations/Marshalling/Marshaller.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index edcd0ac2..be9eb3f3 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -25,10 +25,12 @@ private static CodeFactory CreateDictionaryMethod(ITypeSymbol typeSymbol, Func Date: Sat, 19 Apr 2025 10:02:58 +0200 Subject: [PATCH 23/31] Fix bug --- .../Generations/UnMarshaller.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index aa193718..570ca800 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -83,7 +83,7 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func CreateSignature(type, options) From d89deebff14cad37382fe7dc4576c73615dc4ab2 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Sat, 19 Apr 2025 10:22:54 +0200 Subject: [PATCH 24/31] Fix special syntax --- src/DynamoDBGenerator.SourceGenerator/Constants.cs | 1 + .../Generations/Marshalling/Marshaller.cs | 2 +- src/DynamoDBGenerator/Internal/MarshallHelper.cs | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Constants.cs b/src/DynamoDBGenerator.SourceGenerator/Constants.cs index 87cf7dc7..d50bf8fa 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Constants.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Constants.cs @@ -81,6 +81,7 @@ public static class Marshaller public static class AttributeValueUtilityFactory { private const string ClassName = "MarshallHelper"; + public const string ToAttributeValue = $"{ClassName}.ToAttributeValue"; public const string Null = $"{ClassName}.Null"; public const string ToList = $"{ClassName}.ToList"; public const string ToArray = $"{ClassName}.ToArray"; diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index be9eb3f3..fea8f5d3 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -216,7 +216,7 @@ internal static string InvokeMarshallerMethod(ITypeSymbol typeSymbol, string par if (typeSymbol.TypeIdentifier() is UnknownType) return typeSymbol.IsNullable() - ? $"{invocation} switch {{ {{ }} x => new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ M = x }}, null => null }}" + ? $"{AttributeValueUtilityFactory.ToAttributeValue}({invocation})" : $"new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ M = {invocation} }}"; return invocation; diff --git a/src/DynamoDBGenerator/Internal/MarshallHelper.cs b/src/DynamoDBGenerator/Internal/MarshallHelper.cs index 030a43d8..870c3952 100644 --- a/src/DynamoDBGenerator/Internal/MarshallHelper.cs +++ b/src/DynamoDBGenerator/Internal/MarshallHelper.cs @@ -17,6 +17,9 @@ public static class MarshallHelper #pragma warning disable CS1591 public static AttributeValue Null { get; } = new() { NULL = true }; + public static AttributeValue? ToAttributeValue(Dictionary? dict) => dict is null + ? null + : new AttributeValue { M = dict }; public static AttributeValue FromDictionary( IEnumerable> dictionary, From e87895a771d65ae636ec67d449fa7aed5c8ea102 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Sat, 19 Apr 2025 14:35:17 +0200 Subject: [PATCH 25/31] Add additonal ruling --- .../Generations/Marshalling/Marshaller.cs | 4 +--- src/DynamoDBGenerator/Internal/MarshallHelper.cs | 8 ++++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index fea8f5d3..ec7f712d 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -215,9 +215,7 @@ internal static string InvokeMarshallerMethod(ITypeSymbol typeSymbol, string par return invocation; if (typeSymbol.TypeIdentifier() is UnknownType) - return typeSymbol.IsNullable() - ? $"{AttributeValueUtilityFactory.ToAttributeValue}({invocation})" - : $"new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ M = {invocation} }}"; + return $"{AttributeValueUtilityFactory.ToAttributeValue}({invocation})"; return invocation; } diff --git a/src/DynamoDBGenerator/Internal/MarshallHelper.cs b/src/DynamoDBGenerator/Internal/MarshallHelper.cs index 870c3952..b40c0cf0 100644 --- a/src/DynamoDBGenerator/Internal/MarshallHelper.cs +++ b/src/DynamoDBGenerator/Internal/MarshallHelper.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Amazon.DynamoDBv2.Model; using static System.Runtime.InteropServices.CollectionsMarshal; @@ -17,8 +18,11 @@ public static class MarshallHelper #pragma warning disable CS1591 public static AttributeValue Null { get; } = new() { NULL = true }; - public static AttributeValue? ToAttributeValue(Dictionary? dict) => dict is null - ? null + [return: NotNullIfNotNull(nameof(dict))] + public static AttributeValue? ToAttributeValue( + [NotNullIfNotNull(nameof(dict))] Dictionary? dict + ) => dict is null + ? null : new AttributeValue { M = dict }; public static AttributeValue FromDictionary( From 52bbaee6543892c926812c78e498b8042aebdff9 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Sat, 19 Apr 2025 14:46:18 +0200 Subject: [PATCH 26/31] Isolate logic into types --- .../Extensions/TypeExtensions.cs | 33 +------ .../Generations/AttributeExpressionName.cs | 17 ++-- .../Generations/AttributeExpressionValue.cs | 56 ++++++----- .../Generations/Marshalling/Keys.cs | 15 ++- .../Generations/Marshalling/Marshaller.cs | 93 +++++++++---------- .../Generations/UnMarshaller.cs | 79 ++++++++-------- .../Types/CodeFactory.cs | 19 ++-- .../Types/DataMember.cs | 14 +-- .../Types/DynamoDbDataMember.cs | 2 +- .../Types/MarshallerOptions.cs | 18 ++-- .../Types/TypeIdentifier.cs | 80 ++++++++++++++-- 11 files changed, 227 insertions(+), 199 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs index dd7ab126..b7b2a4b5 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs @@ -1,7 +1,4 @@ -using System.Collections; using System.Collections.Concurrent; -using System.Collections.Immutable; -using System.Runtime.InteropServices; using DynamoDBGenerator.SourceGenerator.Types; using Microsoft.CodeAnalysis; @@ -55,16 +52,6 @@ public static Func CacheFactory(IEqualityComparer co private static readonly Dictionary TypeIdentifierDictionary = new(SymbolEqualityComparer.IncludeNullability); - public static bool IsNullable(this ITypeSymbol typeSymbol) => typeSymbol switch - { - { IsReferenceType: true, NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => true, - { IsReferenceType: true, NullableAnnotation: NullableAnnotation.NotAnnotated } => false, - { IsValueType: true, OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => true, - { IsValueType: true } => false, - _ => throw new ArgumentOutOfRangeException( - $"Could not determine nullablity of type '{typeSymbol.ToDisplayString()}'.") - }; - public static TypeIdentifier TypeIdentifier(this ITypeSymbol type) { if (TypeIdentifierDictionary.TryGetValue(type, out var typeIdentifier)) @@ -292,9 +279,9 @@ public static CodeFactory ToConversion(this IEnumerable enumerable) return new CodeFactory(enumerable); } - public static CodeFactory ToConversion(this IEnumerable enumerable, ITypeSymbol typeSymbol) + public static CodeFactory ToConversion(this IEnumerable enumerable, TypeIdentifier typeIdentifier) { - return new CodeFactory(enumerable, new[] { typeSymbol }); + return new CodeFactory(enumerable, new[] { typeIdentifier }); } public static INamedTypeSymbol? TryGetNullableValueType(this ITypeSymbol type) @@ -307,22 +294,6 @@ public static CodeFactory ToConversion(this IEnumerable enumerable, ITyp : null; } - public static bool IsNumeric(this ITypeSymbol typeSymbol) - { - return typeSymbol.SpecialType - is SpecialType.System_Int16 - or SpecialType.System_Byte - or SpecialType.System_Int32 - or SpecialType.System_Int64 - or SpecialType.System_SByte - or SpecialType.System_UInt16 - or SpecialType.System_UInt32 - or SpecialType.System_UInt64 - or SpecialType.System_Decimal - or SpecialType.System_Double - or SpecialType.System_Single; - } - public static DynamoDbDataMember[] GetDynamoDbProperties(this ITypeSymbol symbol) { // A special rule when it comes to Tuples. diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs index d0c287d1..c3379c2d 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionName.cs @@ -21,7 +21,7 @@ internal static IEnumerable CreateClasses(DynamoDBMarshallerArguments[] Func getDynamoDbProperties, MarshallerOptions options) { // Using _comparer can double classes when there's a None nullable property mixed with a nullable property - var hashSet = new HashSet(SymbolEqualityComparer.Default); + var hashSet = new HashSet(TypeIdentifier.Default); return arguments .SelectMany(x => @@ -105,26 +105,25 @@ private static IEnumerable YieldSelector( .CreateScope($"yield return {camelCase};"); } - private static CodeFactory CreateStruct(ITypeSymbol typeSymbol, Func fn, + private static CodeFactory CreateStruct(TypeIdentifier typeIdentifier, Func fn, MarshallerOptions options) { - var dataMembers = fn(typeSymbol) + var dataMembers = fn(typeIdentifier.TypeSymbol) .Select(x => ( - IsUnknown: !options.IsConvertable(x.DataMember.Type) && - x.DataMember.Type.TypeIdentifier() is UnknownType, + IsUnknown: options.IsUnknown(x.DataMember.TypeIdentifier), DDB: x, DbRef: $"#{x.AttributeName}", - AttributeReference: TypeName(x.DataMember.Type), + AttributeReference: TypeName(x.DataMember.TypeIdentifier.TypeSymbol), AttributeInterfaceName: AttributeExpressionNameTrackerInterface )) .ToArray(); - var structName = TypeName(typeSymbol); + var structName = TypeName(typeIdentifier.TypeSymbol); var @class = $"public readonly struct {structName} : {AttributeExpressionNameTrackerInterface}".CreateScope( - TypeContent(typeSymbol, dataMembers, structName)); - return new CodeFactory(@class, dataMembers.Where(x => x.IsUnknown).Select(x => x.DDB.DataMember.Type)); + TypeContent(typeIdentifier.TypeSymbol, dataMembers, structName)); + return new CodeFactory(@class, dataMembers.Where(x => x.IsUnknown).Select(x => x.DDB.DataMember.TypeIdentifier)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs index bba4844b..dd9748f2 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs @@ -20,7 +20,7 @@ public static class AttributeExpressionValue private const string ValueProvider = "valueIdProvider"; private static IEnumerable TypeContents( - ITypeSymbol typeSymbol, + TypeIdentifier typeIdentifier, (bool IsUnknown, DynamoDbDataMember DDB, string AttributeReference, string AttributeInterfaceName)[] dataMembers, string structName, @@ -61,11 +61,11 @@ MarshallerOptions options const string param = "entity"; - var yields = (typeSymbol switch + var yields = (typeIdentifier switch { - _ when typeSymbol.IsNullable() => $"if ({param} is null)".CreateScope( + {IsNullable:true} => $"if ({param} is null)".CreateScope( $"yield return new ({self}.Value, {AttributeValueUtilityFactory.Null});", "yield break;"), - { IsReferenceType: true } => $"if ({param} is null)".CreateScope( + { TypeSymbol.IsReferenceType: true } => $"if ({param} is null)".CreateScope( $"throw {ExceptionHelper.NullExceptionMethod}(\"{structName}\");"), _ => Enumerable.Empty() }) @@ -74,12 +74,12 @@ _ when typeSymbol.IsNullable() => $"if ({param} is null)".CreateScope( .Concat( $"if ({self}.IsValueCreated)" .CreateScope( - $"yield return new ({self}.Value, {Marshaller.InvokeMarshallerMethod(typeSymbol, "entity", $"\"{structName}\"", options, MarshallerOptions.FieldReference)}{HandeNullability(typeSymbol)});" + $"yield return new ({self}.Value, {Marshaller.InvokeMarshallerMethod(typeIdentifier, "entity", $"\"{structName}\"", options, MarshallerOptions.FieldReference)}{HandeNullability(typeIdentifier)});" ) ) ) .ScopeTo( - $"IEnumerable> {interfaceName}.{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerAccessedValues}({typeSymbol.Representation().annotated} entity)"); + $"IEnumerable> {interfaceName}.{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerAccessedValues}({typeIdentifier.AnnotatedRepresenation} entity)"); foreach (var yield in yields) @@ -88,8 +88,8 @@ _ when typeSymbol.IsNullable() => $"if ({param} is null)".CreateScope( yield return $"public override string ToString() => {self}.Value;"; } - private static string? HandeNullability(ITypeSymbol typeSymbol) => - typeSymbol.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null; + private static string? HandeNullability(TypeIdentifier typeSymbol) => + typeSymbol.IsNullable ? $" ?? {AttributeValueUtilityFactory.Null}" : null; private static IEnumerable YieldSelector( (bool IsUnknown, DynamoDbDataMember DDB, string AttributeReference, string AttributeInterfaceName) x, @@ -99,7 +99,7 @@ private static IEnumerable YieldSelector( if (x.IsUnknown) { - return x.DDB.DataMember.Type.NotNullIfStatement( + return x.DDB.DataMember.TypeIdentifier.TypeSymbol.NotNullIfStatement( accessPattern, $"foreach (var x in ({x.DDB.DataMember.Name} as {x.AttributeInterfaceName}).{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerAccessedValues}({accessPattern}))" .CreateScope("yield return x;") @@ -109,9 +109,9 @@ private static IEnumerable YieldSelector( return $"if ({x.DDB.DataMember.NameAsPrivateField}.IsValueCreated)".CreateScope( - x.DDB.DataMember.Type.NotNullIfStatement( + x.DDB.DataMember.TypeIdentifier.TypeSymbol.NotNullIfStatement( accessPattern, - $"yield return new ({x.DDB.DataMember.NameAsPrivateField}.Value, {Marshaller.InvokeMarshallerMethod(x.DDB.DataMember.Type, $"entity.{x.DDB.DataMember.Name}", $"\"{x.DDB.DataMember.Name}\"", options, MarshallerOptions.FieldReference)}{HandeNullability(x.DDB.DataMember.Type)});" + $"yield return new ({x.DDB.DataMember.NameAsPrivateField}.Value, {Marshaller.InvokeMarshallerMethod(x.DDB.DataMember.TypeIdentifier, $"entity.{x.DDB.DataMember.Name}", $"\"{x.DDB.DataMember.Name}\"", options, MarshallerOptions.FieldReference)}{HandeNullability(x.DDB.DataMember.TypeIdentifier)});" )); } @@ -119,44 +119,40 @@ internal static IEnumerable CreateClasses(DynamoDBMarshallerArguments[] Func getDynamoDbProperties, MarshallerOptions options) { // Using _comparer can double classes when there's a None nullable property mixed with a nullable property - var hashSet = new HashSet(SymbolEqualityComparer.Default); + var hashSet = new HashSet(TypeIdentifier.Default); return arguments .SelectMany(x => CodeFactory.Create(x.ArgumentType, y => CreateStruct(y, getDynamoDbProperties, options), hashSet)); } - private static CodeFactory CreateStruct(ITypeSymbol typeSymbol, Func fn, + private static CodeFactory CreateStruct(TypeIdentifier typeIdentifier, Func fn, MarshallerOptions options) { var dataMembers = - options.IsConvertable(typeSymbol) + options.IsConvertable(typeIdentifier.TypeSymbol) ? Array .Empty<(bool IsUnknown, DynamoDbDataMember DDB, string AttributeReference, string AttributeInterfaceName)>() - : fn(typeSymbol) - .Select(x => - { - return ( - IsUnknown: !options.IsConvertable(x.DataMember.Type) && - x.DataMember.Type.TypeIdentifier() is UnknownType, - DDB: x, - AttributeReference: TypeName(x.DataMember.Type), - AttributeInterfaceName: - $"{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerInterface}<{x.DataMember.Type.Representation().annotated}>" - ); - }) + : fn(typeIdentifier.TypeSymbol) + .Select(x => ( + IsUnknown: options.IsUnknown(x.DataMember.TypeIdentifier), + DDB: x, + AttributeReference: TypeName(x.DataMember.TypeIdentifier.TypeSymbol), + AttributeInterfaceName: + $"{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerInterface}<{x.DataMember.TypeIdentifier.AnnotatedRepresenation}>" + )) .ToArray(); - var structName = TypeName(typeSymbol); + var structName = TypeName(typeIdentifier.TypeSymbol); var interfaceName = - $"{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerInterface}<{typeSymbol.Representation().annotated}>"; + $"{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerInterface}<{typeIdentifier.AnnotatedRepresenation}>"; var @struct = - $"public readonly struct {structName} : {interfaceName}".CreateScope(TypeContents(typeSymbol, dataMembers, + $"public readonly struct {structName} : {interfaceName}".CreateScope(TypeContents(typeIdentifier, dataMembers, structName, interfaceName, options)); - return new CodeFactory(@struct, dataMembers.Where(x => x.IsUnknown).Select(x => x.DDB.DataMember.Type)); + return new CodeFactory(@struct, dataMembers.Where(x => x.IsUnknown).Select(x => x.DDB.DataMember.TypeIdentifier)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs index ea8e5171..f9c5359c 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs @@ -23,16 +23,15 @@ private static IEnumerable CreateAssignment(string validateReference, st DynamoDbDataMember dataMember, MarshallerOptions options) { const string reference = "value"; - var expectedType = dataMember.DataMember.Type.Representation().original; - var expression = $"{keyReference} is {expectedType} {{ }} {reference}"; + var expression = $"{keyReference} is {dataMember.DataMember.TypeIdentifier.OriginalRepresenation} {{ }} {reference}"; var innerContent = $"if ({expression}) " .CreateScope( - $@"{DictionaryName}.Add(""{dataMember.AttributeName}"", {InvokeMarshallerMethod(dataMember.DataMember.Type, reference, $"nameof({keyReference})", options)});") + $@"{DictionaryName}.Add(""{dataMember.AttributeName}"", {InvokeMarshallerMethod(dataMember.DataMember.TypeIdentifier, reference, $"nameof({keyReference})", options)});") .Concat($"else if ({keyReference} is null) ".CreateScope( $@"throw {ExceptionHelper.KeysArgumentNullExceptionMethod}(""{dataMember.DataMember.Name}"", ""{keyReference}"");")) .Concat("else".CreateScope( - $@"throw {ExceptionHelper.KeysInvalidConversionExceptionMethod}(""{dataMember.DataMember.Name}"", ""{keyReference}"", {keyReference}, ""{expectedType}"");")); + $@"throw {ExceptionHelper.KeysInvalidConversionExceptionMethod}(""{dataMember.DataMember.Name}"", ""{keyReference}"", {keyReference}, ""{dataMember.DataMember.TypeIdentifier.OriginalRepresenation}"");")); return $"if ({validateReference})".CreateScope(innerContent); } @@ -76,7 +75,7 @@ private static IEnumerable MethodBody(ITypeSymbol typeSymbol, internal static IEnumerable CreateKeys(IEnumerable arguments, Func getDynamoDbProperties, MarshallerOptions options) { - var hashSet = new HashSet(SymbolEqualityComparer.IncludeNullability); + var hashSet = new HashSet(TypeIdentifier.Nullable); return arguments .SelectMany(x => CodeFactory.Create(x.EntityTypeSymbol, @@ -148,12 +147,12 @@ internal static string AssignmentRoot(ITypeSymbol typeSymbol) $"new {KeyMarshallerImplementationTypeName}((pk, rk, ipk, irk, dm) => {ClassName}.{MethodName(typeSymbol)}({MarshallerOptions.FieldReference}, pk, rk, ipk, irk, dm))"; } - private static CodeFactory StaticAttributeValueDictionaryKeys(ITypeSymbol typeSymbol, + private static CodeFactory StaticAttributeValueDictionaryKeys(TypeIdentifier typeIdentifier, Func fn, MarshallerOptions options) { var code = - $"public static Dictionary {MethodName(typeSymbol)}({options.FullName} {MarshallerOptions.ParamReference}, object? {PkReference}, object? {RkReference}, bool {EnforcePkReference}, bool {EnforceRkReference}, string? index = null)" - .CreateScope(MethodBody(typeSymbol, fn, options)); + $"public static Dictionary {MethodName(typeIdentifier.TypeSymbol)}({options.FullName} {MarshallerOptions.ParamReference}, object? {PkReference}, object? {RkReference}, bool {EnforcePkReference}, bool {EnforceRkReference}, string? index = null)" + .CreateScope(MethodBody(typeIdentifier.TypeSymbol, fn, options)); return new CodeFactory(code); } diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index ec7f712d..880d9047 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -16,17 +16,16 @@ internal static IEnumerable CreateClass(DynamoDBMarshallerArguments[] ar { return $"file static class {ClassName}".CreateScope(TypeContent(arguments, getDynamoDbProperties, options)); } - private static CodeFactory CreateDictionaryMethod(ITypeSymbol typeSymbol, Func fn, MarshallerOptions options) + private static CodeFactory CreateDictionaryMethod(TypeIdentifier typeIdentifier, Func fn, MarshallerOptions options) { - var properties = fn(typeSymbol) + var properties = fn(typeIdentifier.TypeSymbol) .Select(x => { var accessPattern = $"{ParamReference}.{x.DataMember.Name}"; - var isNullable = x.DataMember.Type.IsNullable(); - var marshallerInvocation = InvokeMarshallerMethod(x.DataMember.Type, accessPattern, $"\"{x.DataMember.Name}\"", options); + var marshallerInvocation = InvokeMarshallerMethod(x.DataMember.TypeIdentifier, accessPattern, $"\"{x.DataMember.Name}\"", options); - var assignment = isNullable + var assignment = x.DataMember.TypeIdentifier.IsNullable ? $"if ({x.DataMember.NameAsCamelCase} is not null)" .CreateScope($"{DictionaryReference}[\"{x.AttributeName}\"] = {x.DataMember.NameAsCamelCase};") .Prepend($"var {x.DataMember.NameAsCamelCase} = {marshallerInvocation};") @@ -34,17 +33,16 @@ private static CodeFactory CreateDictionaryMethod(ITypeSymbol typeSymbol, Func(); - if (isNullable) + if (typeIdentifier.IsNullable) enumerable = $"if ({ParamReference} is null)".CreateScope("return null;"); - else if (typeSymbol.IsReferenceType) + else if (typeIdentifier.TypeSymbol.IsReferenceType) enumerable = $"if ({ParamReference} is null)".CreateScope($"throw {ExceptionHelper.NullExceptionMethod}({DataMember});"); var body = @@ -53,16 +51,16 @@ private static CodeFactory CreateDictionaryMethod(ITypeSymbol typeSymbol, Func{(isNullable ? '?' : null)} {GetSerializationMethodName(typeSymbol)}({typeSymbol.Representation().annotated} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)" + $"public static Dictionary{(typeIdentifier.IsNullable ? '?' : null)} {GetSerializationMethodName(typeIdentifier.TypeSymbol)}({typeIdentifier.AnnotatedRepresenation} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)" .CreateScope(body); - return new CodeFactory(code, properties.Select(y => y.Type)); + return new CodeFactory(code, properties.Select(y => y.DataMember.TypeIdentifier)); } private static IEnumerable TypeContent(DynamoDBMarshallerArguments[] arguments, Func getDynamoDbProperties, MarshallerOptions options) { - var hashset = new HashSet(SymbolEqualityComparer.IncludeNullability); + var hashset = new HashSet(TypeIdentifier.Nullable); return arguments.SelectMany(x => CodeFactory .Create( @@ -75,45 +73,44 @@ private static IEnumerable TypeContent(DynamoDBMarshallerArguments[] arg ) .Concat(KeyMarshaller.CreateKeys(arguments, getDynamoDbProperties, options)); } - private static CodeFactory CreateMethod(ITypeSymbol type, Func fn, MarshallerOptions options) + private static CodeFactory CreateMethod(TypeIdentifier typeIdentifier, Func fn, MarshallerOptions options) { - if (options.TryWriteConversion(type, ParamReference) is {} conversion) + if (options.TryWriteConversion(typeIdentifier.TypeSymbol, ParamReference) is {} conversion) { - return type switch + return typeIdentifier.TypeSymbol switch { - { IsValueType: true } => type switch + { IsValueType: true } => typeIdentifier.TypeSymbol switch { - { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => CreateSignature(type, options) + { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => CreateSignature(typeIdentifier, options) .CreateScope($"if ({ParamReference} is null)" .CreateScope("return null;") .Append($"return {conversion};") ) .ToConversion(), - _ => CreateSignature(type, options) + _ => CreateSignature(typeIdentifier, options) .CreateScope($"return {conversion};") .ToConversion() }, - { IsReferenceType: true } => type switch + { IsReferenceType: true } => typeIdentifier.TypeSymbol switch { - { NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => CreateSignature( - type, options) + { NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => CreateSignature(typeIdentifier, options) .CreateScope($"if ({ParamReference} is null)".CreateScope("return null;") .Append($"return {conversion};")) .ToConversion(), - _ => CreateSignature(type, options) + _ => CreateSignature(typeIdentifier, options) .CreateScope( $"if ({ParamReference} is null)".CreateScope($"throw {ExceptionHelper.NullExceptionMethod}({DataMember});").Append($"return {conversion};") ) .ToConversion() }, _ => throw new ArgumentException( - $"Neither ValueType or ReferenceType could be resolved for conversion. type '{type.ToDisplayString()}'.") + $"Neither ValueType or ReferenceType could be resolved for conversion. type '{typeIdentifier.TypeSymbol.ToDisplayString()}'.") }; } - return type.TypeIdentifier() switch + return typeIdentifier switch { - SingleGeneric singleGeneric when CreateSignature(singleGeneric.TypeSymbol, options) is var signature => singleGeneric.Type switch + SingleGeneric singleGeneric when CreateSignature(singleGeneric, options) is var signature => singleGeneric.Type switch { SingleGeneric.SupportedType.Nullable => signature .CreateScope( @@ -125,13 +122,13 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + .Append($"return {AttributeValueUtilityFactory.FromArray}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.List => signature .CreateScope( $"if ({ParamReference} is null)" .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) - .Append($"return {AttributeValueUtilityFactory.FromList}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + .Append($"return {AttributeValueUtilityFactory.FromList}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), SingleGeneric.SupportedType.IReadOnlyCollection or SingleGeneric.SupportedType.IEnumerable @@ -139,17 +136,17 @@ or SingleGeneric.SupportedType.IEnumerable .CreateScope( $"if ({ParamReference} is null)" .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) - .Append($"return {AttributeValueUtilityFactory.FromEnumerable}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + .Append($"return {AttributeValueUtilityFactory.FromEnumerable}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(singleGeneric.T, "a", "d", options, "o")}{(singleGeneric.T.IsNullable ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ).ToConversion(singleGeneric.T), - SingleGeneric.SupportedType.Set when singleGeneric.T.SpecialType is SpecialType.System_String + SingleGeneric.SupportedType.Set when singleGeneric.T.TypeSymbol.SpecialType is SpecialType.System_String => signature .CreateScope( $"if ({ParamReference} is null)" .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) - .Append($"return new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ SS = new List<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>({(singleGeneric.T.IsNullable() ? ParamReference : $"{ParamReference}.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\"))")})}};") + .Append($"return new {Constants.AWSSDK_DynamoDBv2.AttributeValue} {{ SS = new List<{(singleGeneric.T.IsNullable ? "string?" : "string")}>({(singleGeneric.T.IsNullable ? ParamReference : $"{ParamReference}.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\"))")})}};") ) .ToConversion(singleGeneric.T), - SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() + SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric => signature .CreateScope( $"if ({ParamReference} is null)" @@ -162,35 +159,36 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() }, KeyValueGeneric {TKey.SpecialType: not SpecialType.System_String} keyValueGeneric => throw new ArgumentException("Only strings are supported for for TKey", UncoveredConversionException(keyValueGeneric, nameof(CreateMethod))), - KeyValueGeneric keyValueGeneric when CreateSignature(keyValueGeneric.TypeSymbol, options) is var signature => keyValueGeneric.Type switch + KeyValueGeneric keyValueGeneric when CreateSignature(keyValueGeneric, options) is var signature => keyValueGeneric.Type switch { KeyValueGeneric.SupportedType.Dictionary => signature .CreateScope( $"if ({ParamReference} is null)" .CreateScope(keyValueGeneric.ReturnNullOrThrow(DataMember)) - .Append($"return {AttributeValueUtilityFactory.FromDictionary}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + .Append($"return {AttributeValueUtilityFactory.FromDictionary}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ) .ToConversion(keyValueGeneric.TValue), KeyValueGeneric.SupportedType.LookUp => signature .CreateScope( $"if ({ParamReference} is null)" .CreateScope(keyValueGeneric.ReturnNullOrThrow(DataMember)) - .Append($"return {AttributeValueUtilityFactory.FromLookup}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable() ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") + .Append($"return {AttributeValueUtilityFactory.FromLookup}({ParamReference}, {MarshallerOptions.ParamReference}, {DataMember}, static (a, o, d) => {InvokeMarshallerMethod(keyValueGeneric.TValue, "a", "d", options, "o")}{(keyValueGeneric.TValue.IsNullable ? $" ?? {AttributeValueUtilityFactory.Null}" : null)});") ) .ToConversion(keyValueGeneric.TValue), _ => throw UncoveredConversionException(keyValueGeneric, nameof(CreateMethod)) }, - UnknownType unknownType => CreateDictionaryMethod(unknownType.TypeSymbol, fn, options), - var typeIdentifier => throw UncoveredConversionException(typeIdentifier, nameof(CreateMethod)) + UnknownType unknownType => CreateDictionaryMethod(unknownType, fn, options), + _ => throw UncoveredConversionException(typeIdentifier, nameof(CreateMethod)) }; } - private static string CreateSignature(ITypeSymbol typeSymbol, MarshallerOptions options) + private static string CreateSignature(TypeIdentifier typeIdentifier, MarshallerOptions options) { - return typeSymbol.IsNullable() - ? $"public static {Constants.AWSSDK_DynamoDBv2.AttributeValue}? {GetSerializationMethodName(typeSymbol)}({typeSymbol.Representation().annotated} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)" - : $"public static {Constants.AWSSDK_DynamoDBv2.AttributeValue} {GetSerializationMethodName(typeSymbol)}({typeSymbol.Representation().annotated} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)"; + var typeSymbol = typeIdentifier.TypeSymbol; + return typeIdentifier.IsNullable + ? $"public static {Constants.AWSSDK_DynamoDBv2.AttributeValue}? {GetSerializationMethodName(typeSymbol)}({typeIdentifier.AnnotatedRepresenation} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)" + : $"public static {Constants.AWSSDK_DynamoDBv2.AttributeValue} {GetSerializationMethodName(typeSymbol)}({typeIdentifier.AnnotatedRepresenation} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)"; } private static IEnumerable InitializeDictionary(IEnumerable capacityCalculations) @@ -207,17 +205,16 @@ private static IEnumerable InitializeDictionary(IEnumerable capa } } - internal static string InvokeMarshallerMethod(ITypeSymbol typeSymbol, string parameterReference, string dataMember, MarshallerOptions options, string optionParam = MarshallerOptions.ParamReference) + internal static string InvokeMarshallerMethod(TypeIdentifier typeIdentifier, string parameterReference, string dataMember, MarshallerOptions options, string optionParam = MarshallerOptions.ParamReference) { - var invocation = $"{ClassName}.{GetSerializationMethodName(typeSymbol)}({parameterReference}, {optionParam}, {dataMember})"; + var invocation = $"{ClassName}.{GetSerializationMethodName(typeIdentifier.TypeSymbol)}({parameterReference}, {optionParam}, {dataMember})"; - if (options.IsConvertable(typeSymbol)) + if (options.IsConvertable(typeIdentifier.TypeSymbol)) return invocation; - if (typeSymbol.TypeIdentifier() is UnknownType) - return $"{AttributeValueUtilityFactory.ToAttributeValue}({invocation})"; - - return invocation; + return typeIdentifier is UnknownType + ? $"{AttributeValueUtilityFactory.ToAttributeValue}({invocation})" + : invocation; } internal static IEnumerable RootSignature(ITypeSymbol typeSymbol, string rootTypeName) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index 570ca800..6664998b 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -13,9 +13,10 @@ public static class UnMarshaller private static readonly Func GetDeserializationMethodName = TypeExtensions.SuffixedTypeSymbolNameFactory(null, SymbolEqualityComparer.IncludeNullability); private const string UnMarshallerClass = $"_{Marshaller.UnmarshalMethodName}_"; private const string Value = "attributeValue"; - private static IEnumerable<(bool useParentheses, IEnumerable assignments)> Assignments(ITypeSymbol type, (DynamoDbDataMember DDB, string MethodCall, string Name)[] assignments) + private static IEnumerable<(bool useParentheses, IEnumerable assignments)> Assignments(TypeIdentifier typeIdentifier, (DynamoDbDataMember DDB, string MethodCall, string Name)[] assignments) { const string indent = " "; + var type = typeIdentifier.TypeSymbol; if (type.IsTupleType) yield return (true, assignments.Select(x => $"{indent}{x.DDB.DataMember.Name}: {x.MethodCall}")); else @@ -46,47 +47,45 @@ internal static IEnumerable CreateClass(DynamoDBMarshallerArguments[] ar { return $"file static class {UnMarshallerClass}".CreateScope(CreateTypeContents(arguments, getDynamoDbProperties, options)); } - private static CodeFactory CreateCode(ITypeSymbol type, Func fn, MarshallerOptions options) + private static CodeFactory CreateCode(TypeIdentifier typeIdentifier, Func fn, MarshallerOptions options) { - var assignments = fn(type) - .Select(x => (DDB: x, MethodCall: InvokeUnmarshallMethod(x.DataMember.Type, $"{Dict}.GetValueOrDefault(\"{x.AttributeName}\")", $"\"{x.DataMember.Name}\"", options), x.DataMember.Name)) + var assignments = fn(typeIdentifier.TypeSymbol) + .Select(x => (DDB: x, MethodCall: InvokeUnmarshallMethod(x.DataMember.TypeIdentifier, $"{Dict}.GetValueOrDefault(\"{x.AttributeName}\")", $"\"{x.DataMember.Name}\"", options), x.DataMember.Name)) .ToArray(); - var typeName = type.Representation(); var blockBody = $"if ({Dict} is null)" - .CreateScope(type.IsNullable() ? "return null;" : $"throw {ExceptionHelper.NullExceptionMethod}({DataMember});").Concat( - Assignments(type, assignments) + .CreateScope(typeIdentifier.IsNullable ? "return null;" : $"throw {ExceptionHelper.NullExceptionMethod}({DataMember});").Concat( + Assignments(typeIdentifier, assignments) .AllAndLast(x => ObjectAssignmentBlock(x.useParentheses, x.assignments, false), x => ObjectAssignmentBlock(x.useParentheses, x.assignments, true)) .SelectMany(x => x) .DefaultIfEmpty("();") // Is needed in order to not perform new entity? where '?' is not allowed in the end of the string. - .Prepend(type.IsTupleType ? "return" : $"return new {typeName.annotated.TrimEnd('?')}") + .Prepend(typeIdentifier.TypeSymbol.IsTupleType ? "return" : $"return new {typeIdentifier.AnnotatedRepresenation.TrimEnd('?')}") ); - var method = $"public static {typeName.annotated} {GetDeserializationMethodName(type)}(Dictionary? {Dict}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)".CreateScope(blockBody); + var method = $"public static {typeIdentifier.AnnotatedRepresenation} {GetDeserializationMethodName(typeIdentifier.TypeSymbol)}(Dictionary? {Dict}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)".CreateScope(blockBody); - return new CodeFactory(method, assignments.Select(x => x.DDB.DataMember.Type)); + return new CodeFactory(method, assignments.Select(x => x.DDB.DataMember.TypeIdentifier)); } - private static CodeFactory CreateMethod(ITypeSymbol type, Func fn, + private static CodeFactory CreateMethod(TypeIdentifier typeIdentifier, Func fn, MarshallerOptions options) { - - if (options.TryReadConversion(type, Value) is {} conversion) + if (options.TryReadConversion(typeIdentifier, Value) is {} conversion) { - return type switch + return typeIdentifier.TypeSymbol switch { - { IsValueType: true } => type switch + { IsValueType: true } => typeIdentifier.TypeSymbol switch { - { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => CreateSignature(type, options) + { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => CreateSignature(typeIdentifier, options) .CreateScope( $"if ({Value} is null)" .CreateScope("return null;") .Append($"return {conversion};") ) .ToConversion(), - _ => CreateSignature(type, options) + _ => CreateSignature(typeIdentifier, options) .CreateScope( $"if ({Value} is null)" .CreateScope($"throw {ExceptionHelper.NullExceptionMethod}({DataMember});") @@ -100,16 +99,16 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func type switch + { IsReferenceType: true } => typeIdentifier.TypeSymbol switch { { NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => CreateSignature( - type, options) + typeIdentifier, options) .CreateScope( $"if ({Value} is null)" .CreateScope("return null;") .Append($"return {conversion};") ).ToConversion(), - _ => CreateSignature(type, options) + _ => CreateSignature(typeIdentifier, options) .CreateScope( $"if ({Value} is null)" .CreateScope($"throw {ExceptionHelper.NullExceptionMethod}({DataMember});") @@ -125,14 +124,14 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func throw new ArgumentException( - $"Neither ValueType or ReferenceType could be resolved for conversion. type '{type.ToDisplayString()}'." + $"Neither ValueType or ReferenceType could be resolved for conversion. type '{typeIdentifier.TypeSymbol.ToDisplayString()}'." ) }; } - return type.TypeIdentifier() switch + return typeIdentifier switch { - SingleGeneric singleGeneric when CreateSignature(singleGeneric.TypeSymbol, options) is var signature => singleGeneric.Type switch + SingleGeneric singleGeneric when CreateSignature(singleGeneric, options) is var signature => singleGeneric.Type switch { SingleGeneric.SupportedType.Nullable => signature .CreateScope( @@ -162,26 +161,26 @@ private static CodeFactory CreateMethod(ITypeSymbol type, Func {InvokeUnmarshallMethod(singleGeneric.T, "a", "d", options, "o")});") ) .ToConversion(singleGeneric.T), - SingleGeneric.SupportedType.Set when singleGeneric.T.SpecialType is SpecialType.System_String => signature + SingleGeneric.SupportedType.Set when singleGeneric.T.TypeSymbol.SpecialType is SpecialType.System_String => signature .CreateScope( $"if ({Value} is null || {Value}.SS is null)" .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) - .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{(singleGeneric.T.IsNullable() ? "string?" : "string")}>" : null)}({(singleGeneric.T.IsNullable() ? $"{Value}.SS" : $"{Value}.SS.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\")")}));") + .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{(singleGeneric.T.IsNullable ? "string?" : "string")}>" : null)}({(singleGeneric.T.IsNullable ? $"{Value}.SS" : $"{Value}.SS.Select((y,i) => y ?? throw {ExceptionHelper.NullExceptionMethod}($\"{{{DataMember}}}[UNKNOWN]\")")}));") ) .ToConversion(), - SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature + SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric => signature .CreateScope( $"if ({Value} is null || {Value}.NS is null)" .CreateScope(singleGeneric.ReturnNullOrThrow(DataMember)) - .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{singleGeneric.T.Representation().original}>" : null)}({Value}.NS.Select(y => {singleGeneric.T.Representation().original}.Parse(y)));") + .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{singleGeneric.T.OriginalRepresenation}>" : null)}({Value}.NS.Select(y => {singleGeneric.T.OriginalRepresenation}.Parse(y)));") ) - .ToConversion(singleGeneric.TypeSymbol), + .ToConversion(singleGeneric), SingleGeneric.SupportedType.Set => throw new ArgumentException("Only string and integers are supported for sets", UncoveredConversionException(singleGeneric, nameof(CreateMethod))), _ => throw UncoveredConversionException(singleGeneric, nameof(CreateMethod)) }, KeyValueGeneric {TKey.SpecialType: not SpecialType.System_String} keyValueGeneric => throw new ArgumentException("Only strings are supported for for TKey", UncoveredConversionException(keyValueGeneric, nameof(CreateMethod))), - KeyValueGeneric keyValueGeneric when CreateSignature(keyValueGeneric.TypeSymbol, options) is var signature => keyValueGeneric.Type switch + KeyValueGeneric keyValueGeneric when CreateSignature(keyValueGeneric, options) is var signature => keyValueGeneric.Type switch { KeyValueGeneric.SupportedType.Dictionary => signature .CreateScope( @@ -200,21 +199,21 @@ SingleGeneric.SupportedType.Set when singleGeneric.T.IsNumeric() => signature _ => throw UncoveredConversionException(keyValueGeneric, nameof(CreateMethod)) }, - UnknownType => CreateCode(type, fn, options), - var typeIdentifier => throw UncoveredConversionException(typeIdentifier, nameof(CreateMethod)) + UnknownType => CreateCode(typeIdentifier, fn, options), + _ => throw UncoveredConversionException(typeIdentifier, nameof(CreateMethod)) }; } - private static string CreateSignature(ITypeSymbol typeSymbol, MarshallerOptions options) + private static string CreateSignature(TypeIdentifier typeIdentifier, MarshallerOptions options) { - return $"public static {typeSymbol.Representation().annotated} {GetDeserializationMethodName(typeSymbol)}(AttributeValue? {Value}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)"; + return $"public static {typeIdentifier.AnnotatedRepresenation} {GetDeserializationMethodName(typeIdentifier.TypeSymbol)}(AttributeValue? {Value}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)"; } private static IEnumerable CreateTypeContents(IEnumerable arguments, Func getDynamoDbProperties, MarshallerOptions options) { - var hashSet = new HashSet(SymbolEqualityComparer.IncludeNullability); + var hashSet = new HashSet(TypeIdentifier.Nullable); return arguments.SelectMany(x => CodeFactory.Create( x.EntityTypeSymbol, @@ -224,14 +223,14 @@ private static IEnumerable CreateTypeContents(IEnumerable ObjectAssignmentBlock(bool useParentheses, IEnumerable assignments, bool applySemiColon) diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/CodeFactory.cs b/src/DynamoDBGenerator.SourceGenerator/Types/CodeFactory.cs index 0266b834..8919580a 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/CodeFactory.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/CodeFactory.cs @@ -1,10 +1,11 @@ +using DynamoDBGenerator.SourceGenerator.Extensions; using Microsoft.CodeAnalysis; namespace DynamoDBGenerator.SourceGenerator.Types; public readonly struct CodeFactory { - public CodeFactory(IEnumerable lines, IEnumerable dependantTypes) + public CodeFactory(IEnumerable lines, IEnumerable dependantTypes) { _lines = lines; _dependantTypes = dependantTypes; @@ -13,7 +14,7 @@ public CodeFactory(IEnumerable lines, IEnumerable dependant public CodeFactory(IEnumerable lines) { _lines = lines; - _dependantTypes = Enumerable.Empty(); + _dependantTypes = Enumerable.Empty(); } /// @@ -24,12 +25,18 @@ public CodeFactory(IEnumerable lines) /// /// The types that are are dependant on. /// - private readonly IEnumerable _dependantTypes; + private readonly IEnumerable _dependantTypes; public static IEnumerable Create( ITypeSymbol typeSymbol, - Func codeSelector, - ISet handledTypes + Func codeSelector, + ISet handledTypes + ) => Execute(typeSymbol.TypeIdentifier(), codeSelector, handledTypes); + + private static IEnumerable Execute( + TypeIdentifier typeSymbol, + Func codeSelector, + ISet handledTypes ) { // We already support the type. @@ -42,7 +49,7 @@ ISet handledTypes yield return s; foreach (var nestedTypeSymbol in code._dependantTypes) - foreach (var nestedCode in Create(nestedTypeSymbol, codeSelector, handledTypes)) + foreach (var nestedCode in Execute(nestedTypeSymbol, codeSelector, handledTypes)) yield return nestedCode; } } \ No newline at end of file diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/DataMember.cs b/src/DynamoDBGenerator.SourceGenerator/Types/DataMember.cs index 28785234..316cc8f2 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/DataMember.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/DataMember.cs @@ -11,17 +11,16 @@ public readonly struct DataMember private DataMember(in ISymbol symbol, in string fieldName, in ITypeSymbol type, in bool isAssignable) { Name = fieldName; - Type = type; BaseSymbol = symbol; IsAssignable = isAssignable; - NameAsPrivateField = fieldName.ToPrivateFieldFromPascal(); NameAsCamelCase = fieldName.ToCamelCaseFromPascal(); + TypeIdentifier = type.TypeIdentifier(); } public static DataMember FromField(in IFieldSymbol fieldSymbol) { - var symbol = (ISymbol)fieldSymbol; + ISymbol symbol = fieldSymbol; var name = fieldSymbol.Name; var type = fieldSymbol.Type; @@ -30,7 +29,7 @@ public static DataMember FromField(in IFieldSymbol fieldSymbol) public static DataMember FromProperty(in IPropertySymbol property) { - var symbol = (ISymbol)property; + ISymbol symbol = property; var name = property.Name; var type = property.Type; @@ -47,13 +46,8 @@ public static DataMember FromProperty(in IPropertySymbol property) /// public string Name { get; } - + public TypeIdentifier TypeIdentifier { get; } public string NameAsPrivateField { get; } public string NameAsCamelCase { get; } - - /// - /// The type of the data member. - /// - public ITypeSymbol Type { get; } public bool IsAssignable { get; } } diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/DynamoDbDataMember.cs b/src/DynamoDBGenerator.SourceGenerator/Types/DynamoDbDataMember.cs index c4b27f5e..e10480f0 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/DynamoDbDataMember.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/DynamoDbDataMember.cs @@ -96,7 +96,7 @@ public static bool IsIgnored(AttributeData[] attributes) when GetStringArrayFromConstructor(x.Attribute).FirstOrDefault() is { } index => index, Constants.AWSSDK_DynamoDBv2.Attribute.DynamoDBGlobalSecondaryIndexRangeKey when GetStringArrayFromConstructor(x.Attribute).FirstOrDefault() is { } index => index, - _ => throw new NotSupportedException(x.DataMember.DataMember.Type.ToDisplayString()) + _ => throw new NotSupportedException(x.DataMember.DataMember.TypeIdentifier.TypeSymbol.ToDisplayString()) }) .Select(x => { diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs b/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs index f4d4cd2d..a723e5eb 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs @@ -17,7 +17,10 @@ public readonly struct MarshallerOptions public string FullName { get; } public string FieldDeclaration { get; } - + public bool IsUnknown(TypeIdentifier typeSymbol) => + typeSymbol is UnknownType && !IsConvertable(typeSymbol.TypeSymbol); + + private MarshallerOptions( INamedTypeSymbol originalType, INamedTypeSymbol convertersType, @@ -64,23 +67,22 @@ int enumStrategy return null; } - public string? TryReadConversion(ITypeSymbol typeSymbol, string attributeValueParam) + public string? TryReadConversion(TypeIdentifier typeIdentifier, string attributeValueParam) { // Converters comes first so that you your customized converters are always prioritized. - if (Converters.TryGetValue(typeSymbol, out var match)) + if (Converters.TryGetValue(typeIdentifier.TypeSymbol, out var match)) return $"{ParamReference}.{ConvertersProperty}.{match.Key}.Read({attributeValueParam})"; - if (typeSymbol.TypeKind is TypeKind.Enum) + if (typeIdentifier.TypeSymbol.TypeKind is TypeKind.Enum) { - var original = typeSymbol.Representation().original; return _enumStrategy switch { - ConversionStrategy.Integer => $"(Int32.TryParse({attributeValueParam}.N, out var e) ? ({original}?) e : null)", - ConversionStrategy.Name => $"(Enum.TryParse<{original}>({attributeValueParam}.S, false, out var e) ? ({original}?) e : null)", + ConversionStrategy.Integer => $"(Int32.TryParse({attributeValueParam}.N, out var e) ? ({typeIdentifier.OriginalRepresenation}?) e : null)", + ConversionStrategy.Name => $"(Enum.TryParse<{typeIdentifier.OriginalRepresenation}>({attributeValueParam}.S, false, out var e) ? ({typeIdentifier.OriginalRepresenation}?) e : null)", ConversionStrategy.NameCI or ConversionStrategy.LowerCase or ConversionStrategy.UpperCase - => $"(Enum.TryParse<{original}>({attributeValueParam}.S, true, out var e) ? ({original}?) e : null)", + => $"(Enum.TryParse<{typeIdentifier.OriginalRepresenation}>({attributeValueParam}.S, true, out var e) ? ({typeIdentifier.OriginalRepresenation}?) e : null)", _ => throw new ArgumentException($"Could not resolve enum conversion strategy from value '{_enumStrategy}'.") }; } diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs b/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs index f1ed9dd5..46f724ea 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs @@ -3,9 +3,74 @@ namespace DynamoDBGenerator.SourceGenerator.Types; -public abstract record TypeIdentifier(ITypeSymbol TypeSymbol) +public abstract record TypeIdentifier { - private bool IsNullable { get; } = TypeSymbol.IsNullable(); + protected TypeIdentifier(ITypeSymbol typeSymbol) + { + IsNullable = typeSymbol switch + { + { IsReferenceType: true, NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => true, + { IsReferenceType: true, NullableAnnotation: NullableAnnotation.NotAnnotated } => false, + { IsValueType: true, OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => true, + { IsValueType: true } => false, + _ => throw new ArgumentOutOfRangeException( + $"Could not determine nullablity of type '{typeSymbol.ToDisplayString()}'.") + }; + + TypeSymbol = typeSymbol; + var (annotated, original) = typeSymbol.Representation(); + OriginalRepresenation = original; + AnnotatedRepresenation = annotated; + IsNumeric = IsNumericMethod(typeSymbol); + } + + internal static readonly IEqualityComparer Default = + new TypeSymbolDelegator(SymbolEqualityComparer.Default); + + internal static readonly IEqualityComparer Nullable = + new TypeSymbolDelegator(SymbolEqualityComparer.IncludeNullability); + + private class TypeSymbolDelegator : IEqualityComparer + { + private readonly IEqualityComparer _comparer; + + public TypeSymbolDelegator(IEqualityComparer comparer) + { + _comparer = comparer; + } + + public bool Equals(TypeIdentifier? x, TypeIdentifier? y) + { + return ReferenceEquals(x, y) || x switch + { + null => false, + _ => y is not null && _comparer.Equals(x.TypeSymbol, y.TypeSymbol) + }; + } + + public int GetHashCode(TypeIdentifier obj) => _comparer.GetHashCode(obj.TypeSymbol); + } + + private static bool IsNumericMethod(ITypeSymbol typeSymbol) + { + return typeSymbol.SpecialType + is SpecialType.System_Int16 + or SpecialType.System_Byte + or SpecialType.System_Int32 + or SpecialType.System_Int64 + or SpecialType.System_SByte + or SpecialType.System_UInt16 + or SpecialType.System_UInt32 + or SpecialType.System_UInt64 + or SpecialType.System_Decimal + or SpecialType.System_Double + or SpecialType.System_Single; + } + + public bool IsNullable { get; } + public bool IsNumeric { get; } + public string AnnotatedRepresenation { get; } + public string OriginalRepresenation { get; } public string ReturnNullOrThrow(string dataMember) { @@ -14,7 +79,7 @@ public string ReturnNullOrThrow(string dataMember) : $"throw {Constants.DynamoDBGenerator.ExceptionHelper.NullExceptionMethod}({dataMember});"; } - public ITypeSymbol TypeSymbol { get; } = TypeSymbol; + public ITypeSymbol TypeSymbol { get; } } public sealed record UnknownType(ITypeSymbol TypeSymbol) : TypeIdentifier(TypeSymbol); @@ -32,7 +97,7 @@ private KeyValueGeneric(in ITypeSymbol typeSymbol, in ITypeSymbol tKey, in IType { Type = supportedType; TKey = tKey; - TValue = tValue; + TValue = tValue.TypeIdentifier(); } public SupportedType Type { get; } @@ -41,7 +106,7 @@ private KeyValueGeneric(in ITypeSymbol typeSymbol, in ITypeSymbol tKey, in IType public ITypeSymbol TKey { get; } // ReSharper disable once InconsistentNaming - public ITypeSymbol TValue { get; } + public TypeIdentifier TValue { get; } public static KeyValueGeneric? CreateInstance(in ITypeSymbol typeSymbol) { @@ -80,12 +145,11 @@ private SingleGeneric(ITypeSymbol type, ITypeSymbol innerType, in SupportedType { Type = supportedType; - T = innerType; + T = innerType.TypeIdentifier(); } public SupportedType Type { get; } - public ITypeSymbol T { get; } - + public TypeIdentifier T { get; } public static SingleGeneric? CreateInstance(in ITypeSymbol typeSymbol) { From f82c86c500b75e3325dc774b475eaf0e1a182f02 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Sat, 19 Apr 2025 16:14:14 +0200 Subject: [PATCH 27/31] Move representation method to TypeIdentifier --- .../Extensions/TypeExtensions.cs | 68 --------- .../Generations/AttributeExpressionValue.cs | 6 +- .../Generations/Marshalling/Keys.cs | 4 +- .../Generations/Marshalling/Marshaller.cs | 12 +- .../Generations/UnMarshaller.cs | 14 +- .../MarshallerFactory.cs | 22 +-- .../Types/CodeFactory.cs | 9 +- .../Types/DynamoDBMarshallerArguments.cs | 27 ++-- .../Types/MarshallerOptions.cs | 6 +- .../Types/TypeIdentifier.cs | 130 ++++++++++++++---- 10 files changed, 150 insertions(+), 148 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs index b7b2a4b5..dc5ff5e7 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs @@ -71,75 +71,7 @@ static TypeIdentifier Create(ITypeSymbol typeSymbol) } } - private static readonly ConcurrentDictionary RepresentationDictionary = - new(SymbolEqualityComparer.IncludeNullability); - - public static (string annotated, string original) Representation(this ITypeSymbol typeSymbol) - { - return RepresentationDictionary.GetOrAdd(typeSymbol, x => - { - var displayString = x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - return RepresentationDictionary[typeSymbol] = (ToString(typeSymbol, displayString), displayString); - }); - - static string ToString(ITypeSymbol x, string displayString) - { - if (x is IArrayTypeSymbol arrayTypeSymbol) - { - var result = ToString(arrayTypeSymbol.ElementType, - arrayTypeSymbol.ElementType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); - return x.NullableAnnotation switch - { - NullableAnnotation.Annotated or NullableAnnotation.None => $"{result}[]?", - NullableAnnotation.NotAnnotated => $"{result}[]", - _ => throw new ArgumentException(ExceptionMessage(x)) - }; - } - - if (x is not INamedTypeSymbol namedTypeSymbol || namedTypeSymbol.TypeArguments.Length is 0) - { - return x.NullableAnnotation switch - { - // Having `Annotated` and `None` produce append '?' is fine as long as `SuffixedTypeSymbolNameFactory` is giving them different names. Otherwise we could create broken signatures due to duplication. - NullableAnnotation.Annotated or NullableAnnotation.None => $"{displayString}?", - NullableAnnotation.NotAnnotated => displayString, - _ => throw new ArgumentException(ExceptionMessage(x)) - }; - } - - if (namedTypeSymbol.OriginalDefinition.SpecialType is SpecialType.System_Nullable_T) - return displayString; - - if (namedTypeSymbol.IsTupleType) - { - var tupleElements = namedTypeSymbol.TupleElements - .Select(y => - $"{ToString(y.Type, $"{y.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}")} {y.Name}"); - return $"({string.Join(", ", tupleElements)})"; - } - - var index = displayString.AsSpan().IndexOf('<'); - if (index == -1) - return displayString; - - var typeWithoutGenericParameters = displayString.Substring(0, index); - var typeParameters = string.Join(", ", - namedTypeSymbol.TypeArguments.Select(y => - ToString(y, y.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)))); - return namedTypeSymbol.NullableAnnotation switch - { - // Having `Annotated` and `None` produce append '?' is fine as long as `SuffixedTypeSymbolNameFactory` is giving them different names. Otherwise we could create broken signatures due to duplication. - NullableAnnotation.Annotated or NullableAnnotation.None => - $"{typeWithoutGenericParameters}<{typeParameters}>?", - NullableAnnotation.NotAnnotated => $"{typeWithoutGenericParameters}<{typeParameters}>", - _ => throw new ArgumentException(ExceptionMessage(namedTypeSymbol)) - }; - - static string ExceptionMessage(ISymbol typeSymbol) => - $"Could nullable annotation on type: {typeSymbol.ToDisplayString()}"; - } - } //Source: https://referencesource.microsoft.com/#mscorlib/system/tuple.cs,49b112811bc359fd,references private class TupleComparer : IEqualityComparer<(ITypeSymbol, ITypeSymbol)> diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs index dd9748f2..bc0f4c83 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs @@ -79,7 +79,7 @@ MarshallerOptions options ) ) .ScopeTo( - $"IEnumerable> {interfaceName}.{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerAccessedValues}({typeIdentifier.AnnotatedRepresenation} entity)"); + $"IEnumerable> {interfaceName}.{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerAccessedValues}({typeIdentifier.AnnotatedString} entity)"); foreach (var yield in yields) @@ -140,13 +140,13 @@ private static CodeFactory CreateStruct(TypeIdentifier typeIdentifier, Func" + $"{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerInterface}<{x.DataMember.TypeIdentifier.AnnotatedString}>" )) .ToArray(); var structName = TypeName(typeIdentifier.TypeSymbol); var interfaceName = - $"{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerInterface}<{typeIdentifier.AnnotatedRepresenation}>"; + $"{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerInterface}<{typeIdentifier.AnnotatedString}>"; var @struct = $"public readonly struct {structName} : {interfaceName}".CreateScope(TypeContents(typeIdentifier, dataMembers, diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs index f9c5359c..a0dca750 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Keys.cs @@ -23,7 +23,7 @@ private static IEnumerable CreateAssignment(string validateReference, st DynamoDbDataMember dataMember, MarshallerOptions options) { const string reference = "value"; - var expression = $"{keyReference} is {dataMember.DataMember.TypeIdentifier.OriginalRepresenation} {{ }} {reference}"; + var expression = $"{keyReference} is {dataMember.DataMember.TypeIdentifier.UnannotatedString} {{ }} {reference}"; var innerContent = $"if ({expression}) " .CreateScope( @@ -31,7 +31,7 @@ private static IEnumerable CreateAssignment(string validateReference, st .Concat($"else if ({keyReference} is null) ".CreateScope( $@"throw {ExceptionHelper.KeysArgumentNullExceptionMethod}(""{dataMember.DataMember.Name}"", ""{keyReference}"");")) .Concat("else".CreateScope( - $@"throw {ExceptionHelper.KeysInvalidConversionExceptionMethod}(""{dataMember.DataMember.Name}"", ""{keyReference}"", {keyReference}, ""{dataMember.DataMember.TypeIdentifier.OriginalRepresenation}"");")); + $@"throw {ExceptionHelper.KeysInvalidConversionExceptionMethod}(""{dataMember.DataMember.Name}"", ""{keyReference}"", {keyReference}, ""{dataMember.DataMember.TypeIdentifier.UnannotatedString}"");")); return $"if ({validateReference})".CreateScope(innerContent); } diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index 880d9047..bd8da44c 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -51,7 +51,7 @@ private static CodeFactory CreateDictionaryMethod(TypeIdentifier typeIdentifier, .Append($"return {DictionaryReference};")); var code = - $"public static Dictionary{(typeIdentifier.IsNullable ? '?' : null)} {GetSerializationMethodName(typeIdentifier.TypeSymbol)}({typeIdentifier.AnnotatedRepresenation} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)" + $"public static Dictionary{(typeIdentifier.IsNullable ? '?' : null)} {GetSerializationMethodName(typeIdentifier.TypeSymbol)}({typeIdentifier.AnnotatedString} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)" .CreateScope(body); return new CodeFactory(code, properties.Select(y => y.DataMember.TypeIdentifier)); @@ -187,8 +187,8 @@ private static string CreateSignature(TypeIdentifier typeIdentifier, MarshallerO { var typeSymbol = typeIdentifier.TypeSymbol; return typeIdentifier.IsNullable - ? $"public static {Constants.AWSSDK_DynamoDBv2.AttributeValue}? {GetSerializationMethodName(typeSymbol)}({typeIdentifier.AnnotatedRepresenation} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)" - : $"public static {Constants.AWSSDK_DynamoDBv2.AttributeValue} {GetSerializationMethodName(typeSymbol)}({typeIdentifier.AnnotatedRepresenation} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)"; + ? $"public static {Constants.AWSSDK_DynamoDBv2.AttributeValue}? {GetSerializationMethodName(typeSymbol)}({typeIdentifier.AnnotatedString} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)" + : $"public static {Constants.AWSSDK_DynamoDBv2.AttributeValue} {GetSerializationMethodName(typeSymbol)}({typeIdentifier.AnnotatedString} {ParamReference}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)"; } private static IEnumerable InitializeDictionary(IEnumerable capacityCalculations) @@ -217,13 +217,13 @@ internal static string InvokeMarshallerMethod(TypeIdentifier typeIdentifier, str : invocation; } - internal static IEnumerable RootSignature(ITypeSymbol typeSymbol, string rootTypeName) + internal static IEnumerable RootSignature(TypeIdentifier typeIdentifier) { - return $"public Dictionary<{nameof(String)}, {Constants.AWSSDK_DynamoDBv2.AttributeValue}> {Constants.DynamoDBGenerator.Marshaller.MarshallMethodName}({rootTypeName} {ParamReference})" + return $"public Dictionary<{nameof(String)}, {Constants.AWSSDK_DynamoDBv2.AttributeValue}> {Constants.DynamoDBGenerator.Marshaller.MarshallMethodName}({typeIdentifier.AnnotatedString} {ParamReference})" .CreateScope( $"ArgumentNullException.ThrowIfNull({ParamReference});", - $"return {ClassName}.{GetSerializationMethodName(typeSymbol)}({ParamReference}, {MarshallerOptions.FieldReference});" + $"return {ClassName}.{GetSerializationMethodName(typeIdentifier.TypeSymbol)}({ParamReference}, {MarshallerOptions.FieldReference});" ); } diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index 6664998b..80cca395 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -61,10 +61,10 @@ private static CodeFactory CreateCode(TypeIdentifier typeIdentifier, Func x) .DefaultIfEmpty("();") // Is needed in order to not perform new entity? where '?' is not allowed in the end of the string. - .Prepend(typeIdentifier.TypeSymbol.IsTupleType ? "return" : $"return new {typeIdentifier.AnnotatedRepresenation.TrimEnd('?')}") + .Prepend(typeIdentifier.TypeSymbol.IsTupleType ? "return" : $"return new {typeIdentifier.AnnotatedString.TrimEnd('?')}") ); - var method = $"public static {typeIdentifier.AnnotatedRepresenation} {GetDeserializationMethodName(typeIdentifier.TypeSymbol)}(Dictionary? {Dict}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)".CreateScope(blockBody); + var method = $"public static {typeIdentifier.AnnotatedString} {GetDeserializationMethodName(typeIdentifier.TypeSymbol)}(Dictionary? {Dict}, {options.FullName} {MarshallerOptions.ParamReference}, string? {DataMember} = null)".CreateScope(blockBody); return new CodeFactory(method, assignments.Select(x => x.DDB.DataMember.TypeIdentifier)); @@ -172,7 +172,7 @@ private static CodeFactory CreateMethod(TypeIdentifier typeIdentifier, Func" : null)}({Value}.NS.Select(y => {singleGeneric.T.OriginalRepresenation}.Parse(y)));") + .Append($"return new {(singleGeneric.TypeSymbol.TypeKind is TypeKind.Interface ? $"HashSet<{singleGeneric.T.UnannotatedString}>" : null)}({Value}.NS.Select(y => {singleGeneric.T.UnannotatedString}.Parse(y)));") ) .ToConversion(singleGeneric), SingleGeneric.SupportedType.Set => throw new ArgumentException("Only string and integers are supported for sets", UncoveredConversionException(singleGeneric, nameof(CreateMethod))), @@ -207,7 +207,7 @@ private static CodeFactory CreateMethod(TypeIdentifier typeIdentifier, Func CreateTypeContents(IEnumerable arguments, @@ -264,11 +264,11 @@ private static IEnumerable ObjectAssignmentBlock(bool useParentheses, IE } - internal static IEnumerable RootSignature(ITypeSymbol typeSymbol, string rootTypeName) + internal static IEnumerable RootSignature(TypeIdentifier typeIdentifier) { - return $"public {rootTypeName} {Marshaller.UnmarshalMethodName}(Dictionary<{nameof(String)}, {Constants.AWSSDK_DynamoDBv2.AttributeValue}> entity)".CreateScope( + return $"public {typeIdentifier.AnnotatedString} {Marshaller.UnmarshalMethodName}(Dictionary<{nameof(String)}, {Constants.AWSSDK_DynamoDBv2.AttributeValue}> entity)".CreateScope( "ArgumentNullException.ThrowIfNull(entity);", - $"return {UnMarshallerClass}.{GetDeserializationMethodName(typeSymbol)}(entity, {MarshallerOptions.FieldReference});"); + $"return {UnMarshallerClass}.{GetDeserializationMethodName(typeIdentifier.TypeSymbol)}(entity, {MarshallerOptions.FieldReference});"); } private static IEnumerable<(string DataMember, string ParameterName)> TryGetMatchedConstructorArguments(ITypeSymbol typeSymbol) { diff --git a/src/DynamoDBGenerator.SourceGenerator/MarshallerFactory.cs b/src/DynamoDBGenerator.SourceGenerator/MarshallerFactory.cs index 8e14e071..d9b22a0a 100644 --- a/src/DynamoDBGenerator.SourceGenerator/MarshallerFactory.cs +++ b/src/DynamoDBGenerator.SourceGenerator/MarshallerFactory.cs @@ -17,22 +17,22 @@ private static IEnumerable CreateImplementations( { foreach (var argument in arguments) { - var (expressionValueMethod, valueTrackerTypeName) = AttributeExpressionValue.RootSignature(parentType, argument.ArgumentType); - var (expressionMethodName, nameTrackerTypeName) = AttributeExpressionName.RootSignature(parentType, argument.EntityTypeSymbol); + var (expressionValueMethod, valueTrackerTypeName) = AttributeExpressionValue.RootSignature(parentType, argument.ArgumentType.TypeSymbol); + var (expressionMethodName, nameTrackerTypeName) = AttributeExpressionName.RootSignature(parentType, argument.EntityTypeSymbol.TypeSymbol); var constructor = $"public {argument.ImplementationName}({options.FullName} {MarshallerOptions.ParamReference})" .CreateScope($"{MarshallerOptions.FieldReference} = {MarshallerOptions.ParamReference};", - $"{KeyMarshaller.PrimaryKeyMarshallerReference} = {KeyMarshaller.AssignmentRoot(argument.EntityTypeSymbol)};"); + $"{KeyMarshaller.PrimaryKeyMarshallerReference} = {KeyMarshaller.AssignmentRoot(argument.EntityTypeSymbol.TypeSymbol)};"); var implementation = constructor - .Concat(RootSignature(argument.EntityTypeSymbol, argument.AnnotatedEntityType)) - .Concat(UnMarshaller.RootSignature(argument.EntityTypeSymbol, argument.AnnotatedEntityType)) - .Concat(KeyMarshaller.IndexKeyMarshallerRootSignature(argument.EntityTypeSymbol)) + .Concat(RootSignature(argument.EntityTypeSymbol)) + .Concat(UnMarshaller.RootSignature(argument.EntityTypeSymbol)) + .Concat(KeyMarshaller.IndexKeyMarshallerRootSignature(argument.EntityTypeSymbol.TypeSymbol)) .Concat(expressionValueMethod) .Append(expressionMethodName) .Append(KeyMarshaller.PrimaryKeyMarshallerDeclaration) .Prepend(options.FieldDeclaration) - .ScopeTo($"file sealed class {argument.ImplementationName}: {Interface}<{argument.AnnotatedEntityType}, {argument.AnnotatedArgumentType}, {nameTrackerTypeName}, {valueTrackerTypeName}>"); + .ScopeTo($"file sealed class {argument.ImplementationName}: {Interface}<{argument.EntityTypeSymbol.AnnotatedString}, {argument.ArgumentType.AnnotatedString}, {nameTrackerTypeName}, {valueTrackerTypeName}>"); foreach (var row in implementation) yield return row; @@ -44,12 +44,12 @@ private static IEnumerable PublicProperties(ITypeSymbol parentType, Dyna { foreach (var argument in arguments) { - var valueTrackerTypeName = AttributeExpressionValue.GloballyAccessibleName(parentType, argument.ArgumentType); - var nameTrackerTypeName = AttributeExpressionName.GloballyAccessibleName(parentType, argument.EntityTypeSymbol); + var valueTrackerTypeName = AttributeExpressionValue.GloballyAccessibleName(parentType, argument.ArgumentType.TypeSymbol); + var nameTrackerTypeName = AttributeExpressionName.GloballyAccessibleName(parentType, argument.EntityTypeSymbol.TypeSymbol); yield return options.TryInstantiate() switch { - { } arg => $"public static {Interface}<{argument.AnnotatedEntityType}, {argument.AnnotatedArgumentType}, {nameTrackerTypeName}, {valueTrackerTypeName}> {argument.AccessName} {{ get; }} = new {argument.ImplementationName}({arg});", - null => $"public static {Interface}<{argument.AnnotatedEntityType}, {argument.AnnotatedArgumentType}, {nameTrackerTypeName}, {valueTrackerTypeName}> {argument.AccessName}({options.FullName} options) => new {argument.ImplementationName}(options);" + { } arg => $"public static {Interface}<{argument.EntityTypeSymbol.AnnotatedString}, {argument.ArgumentType.AnnotatedString}, {nameTrackerTypeName}, {valueTrackerTypeName}> {argument.AccessName} {{ get; }} = new {argument.ImplementationName}({arg});", + null => $"public static {Interface}<{argument.EntityTypeSymbol.AnnotatedString}, {argument.ArgumentType.AnnotatedString}, {nameTrackerTypeName}, {valueTrackerTypeName}> {argument.AccessName}({options.FullName} options) => new {argument.ImplementationName}(options);" }; } } diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/CodeFactory.cs b/src/DynamoDBGenerator.SourceGenerator/Types/CodeFactory.cs index 8919580a..ea42919a 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/CodeFactory.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/CodeFactory.cs @@ -28,12 +28,6 @@ public CodeFactory(IEnumerable lines) private readonly IEnumerable _dependantTypes; public static IEnumerable Create( - ITypeSymbol typeSymbol, - Func codeSelector, - ISet handledTypes - ) => Execute(typeSymbol.TypeIdentifier(), codeSelector, handledTypes); - - private static IEnumerable Execute( TypeIdentifier typeSymbol, Func codeSelector, ISet handledTypes @@ -49,7 +43,8 @@ ISet handledTypes yield return s; foreach (var nestedTypeSymbol in code._dependantTypes) - foreach (var nestedCode in Execute(nestedTypeSymbol, codeSelector, handledTypes)) + foreach (var nestedCode in Create(nestedTypeSymbol, codeSelector, handledTypes)) yield return nestedCode; } + } \ No newline at end of file diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/DynamoDBMarshallerArguments.cs b/src/DynamoDBGenerator.SourceGenerator/Types/DynamoDBMarshallerArguments.cs index 4dbcf072..30b413e9 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/DynamoDBMarshallerArguments.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/DynamoDBMarshallerArguments.cs @@ -1,27 +1,26 @@ using DynamoDBGenerator.SourceGenerator.Extensions; using Microsoft.CodeAnalysis; + namespace DynamoDBGenerator.SourceGenerator.Types; public readonly record struct DynamoDBMarshallerArguments { - public DynamoDBMarshallerArguments(INamedTypeSymbol entityTypeSymbol, INamedTypeSymbol? argumentType, string? propertyName) + public DynamoDBMarshallerArguments( + INamedTypeSymbol entityTypeSymbol, + INamedTypeSymbol? argumentType, + string? propertyName + ) { - EntityTypeSymbol = (INamedTypeSymbol)entityTypeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated); + EntityTypeSymbol = entityTypeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated).TypeIdentifier(); ArgumentType = argumentType is null ? EntityTypeSymbol - : (INamedTypeSymbol)argumentType.WithNullableAnnotation(NullableAnnotation.NotAnnotated); - AccessName = propertyName ?? $"{EntityTypeSymbol.Name}Marshaller"; + : argumentType.WithNullableAnnotation(NullableAnnotation.NotAnnotated).TypeIdentifier(); + AccessName = propertyName ?? $"{EntityTypeSymbol.TypeSymbol.Name}Marshaller"; ImplementationName = $"{AccessName}Implementation"; - - AnnotatedEntityType = EntityTypeSymbol.Representation().annotated; - AnnotatedArgumentType = argumentType is null ? AnnotatedEntityType : ArgumentType.Representation().annotated; - } + public string ImplementationName { get; } - public INamedTypeSymbol EntityTypeSymbol { get; } - public INamedTypeSymbol ArgumentType { get; } + public TypeIdentifier EntityTypeSymbol { get; } + public TypeIdentifier ArgumentType { get; } public string AccessName { get; } - public string AnnotatedEntityType { get; } - public string AnnotatedArgumentType { get; } - -} +} \ No newline at end of file diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs b/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs index a723e5eb..7b3bff78 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs @@ -77,12 +77,12 @@ int enumStrategy { return _enumStrategy switch { - ConversionStrategy.Integer => $"(Int32.TryParse({attributeValueParam}.N, out var e) ? ({typeIdentifier.OriginalRepresenation}?) e : null)", - ConversionStrategy.Name => $"(Enum.TryParse<{typeIdentifier.OriginalRepresenation}>({attributeValueParam}.S, false, out var e) ? ({typeIdentifier.OriginalRepresenation}?) e : null)", + ConversionStrategy.Integer => $"(Int32.TryParse({attributeValueParam}.N, out var e) ? ({typeIdentifier.UnannotatedString}?) e : null)", + ConversionStrategy.Name => $"(Enum.TryParse<{typeIdentifier.UnannotatedString}>({attributeValueParam}.S, false, out var e) ? ({typeIdentifier.UnannotatedString}?) e : null)", ConversionStrategy.NameCI or ConversionStrategy.LowerCase or ConversionStrategy.UpperCase - => $"(Enum.TryParse<{typeIdentifier.OriginalRepresenation}>({attributeValueParam}.S, true, out var e) ? ({typeIdentifier.OriginalRepresenation}?) e : null)", + => $"(Enum.TryParse<{typeIdentifier.UnannotatedString}>({attributeValueParam}.S, true, out var e) ? ({typeIdentifier.UnannotatedString}?) e : null)", _ => throw new ArgumentException($"Could not resolve enum conversion strategy from value '{_enumStrategy}'.") }; } diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs b/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs index 46f724ea..afce9fb5 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/TypeIdentifier.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using DynamoDBGenerator.SourceGenerator.Extensions; using Microsoft.CodeAnalysis; @@ -5,50 +6,108 @@ namespace DynamoDBGenerator.SourceGenerator.Types; public abstract record TypeIdentifier { + private static readonly ConcurrentDictionary RepresentationDictionary = + new(SymbolEqualityComparer.IncludeNullability); + + internal static readonly IEqualityComparer Default = + new TypeSymbolDelegator(SymbolEqualityComparer.Default); + + internal static readonly IEqualityComparer Nullable = + new TypeSymbolDelegator(SymbolEqualityComparer.IncludeNullability); + protected TypeIdentifier(ITypeSymbol typeSymbol) { IsNullable = typeSymbol switch { - { IsReferenceType: true, NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated } => true, + { + IsReferenceType: true, NullableAnnotation: NullableAnnotation.None or NullableAnnotation.Annotated + } => true, { IsReferenceType: true, NullableAnnotation: NullableAnnotation.NotAnnotated } => false, { IsValueType: true, OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } => true, { IsValueType: true } => false, _ => throw new ArgumentOutOfRangeException( $"Could not determine nullablity of type '{typeSymbol.ToDisplayString()}'.") }; - + TypeSymbol = typeSymbol; - var (annotated, original) = typeSymbol.Representation(); - OriginalRepresenation = original; - AnnotatedRepresenation = annotated; + var (annotated, original) = Representation(typeSymbol); + UnannotatedString = original; + AnnotatedString = annotated; IsNumeric = IsNumericMethod(typeSymbol); } - internal static readonly IEqualityComparer Default = - new TypeSymbolDelegator(SymbolEqualityComparer.Default); + public bool IsNullable { get; } + public bool IsNumeric { get; } + public string AnnotatedString { get; } + public string UnannotatedString { get; } - internal static readonly IEqualityComparer Nullable = - new TypeSymbolDelegator(SymbolEqualityComparer.IncludeNullability); + public ITypeSymbol TypeSymbol { get; } - private class TypeSymbolDelegator : IEqualityComparer + private static (string annotated, string original) Representation(ITypeSymbol typeSymbol) { - private readonly IEqualityComparer _comparer; - - public TypeSymbolDelegator(IEqualityComparer comparer) + return RepresentationDictionary.GetOrAdd(typeSymbol, x => { - _comparer = comparer; - } + var displayString = x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + return RepresentationDictionary[typeSymbol] = (ToString(typeSymbol, displayString), displayString); + }); - public bool Equals(TypeIdentifier? x, TypeIdentifier? y) + static string ToString(ITypeSymbol x, string displayString) { - return ReferenceEquals(x, y) || x switch + if (x is IArrayTypeSymbol arrayTypeSymbol) { - null => false, - _ => y is not null && _comparer.Equals(x.TypeSymbol, y.TypeSymbol) + var result = ToString(arrayTypeSymbol.ElementType, + arrayTypeSymbol.ElementType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); + + return x.NullableAnnotation switch + { + NullableAnnotation.Annotated or NullableAnnotation.None => $"{result}[]?", + NullableAnnotation.NotAnnotated => $"{result}[]", + _ => throw new ArgumentException(ExceptionMessage(x)) + }; + } + + if (x is not INamedTypeSymbol namedTypeSymbol || namedTypeSymbol.TypeArguments.Length is 0) + return x.NullableAnnotation switch + { + // Having `Annotated` and `None` produce append '?' is fine as long as `SuffixedTypeSymbolNameFactory` is giving them different names. Otherwise we could create broken signatures due to duplication. + NullableAnnotation.Annotated or NullableAnnotation.None => $"{displayString}?", + NullableAnnotation.NotAnnotated => displayString, + _ => throw new ArgumentException(ExceptionMessage(x)) + }; + + if (namedTypeSymbol.OriginalDefinition.SpecialType is SpecialType.System_Nullable_T) + return displayString; + + if (namedTypeSymbol.IsTupleType) + { + var tupleElements = namedTypeSymbol.TupleElements + .Select(y => + $"{ToString(y.Type, $"{y.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}")} {y.Name}"); + return $"({string.Join(", ", tupleElements)})"; + } + + var index = displayString.AsSpan().IndexOf('<'); + if (index == -1) + return displayString; + + var typeWithoutGenericParameters = displayString.Substring(0, index); + var typeParameters = string.Join(", ", + namedTypeSymbol.TypeArguments.Select(y => + ToString(y, y.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)))); + return namedTypeSymbol.NullableAnnotation switch + { + // Having `Annotated` and `None` produce append '?' is fine as long as `SuffixedTypeSymbolNameFactory` is giving them different names. Otherwise we could create broken signatures due to duplication. + NullableAnnotation.Annotated or NullableAnnotation.None => + $"{typeWithoutGenericParameters}<{typeParameters}>?", + NullableAnnotation.NotAnnotated => $"{typeWithoutGenericParameters}<{typeParameters}>", + _ => throw new ArgumentException(ExceptionMessage(namedTypeSymbol)) }; - } - public int GetHashCode(TypeIdentifier obj) => _comparer.GetHashCode(obj.TypeSymbol); + static string ExceptionMessage(ISymbol typeSymbol) + { + return $"Could nullable annotation on type: {typeSymbol.ToDisplayString()}"; + } + } } private static bool IsNumericMethod(ITypeSymbol typeSymbol) @@ -67,11 +126,6 @@ or SpecialType.System_Double or SpecialType.System_Single; } - public bool IsNullable { get; } - public bool IsNumeric { get; } - public string AnnotatedRepresenation { get; } - public string OriginalRepresenation { get; } - public string ReturnNullOrThrow(string dataMember) { return IsNullable @@ -79,7 +133,29 @@ public string ReturnNullOrThrow(string dataMember) : $"throw {Constants.DynamoDBGenerator.ExceptionHelper.NullExceptionMethod}({dataMember});"; } - public ITypeSymbol TypeSymbol { get; } + private class TypeSymbolDelegator : IEqualityComparer + { + private readonly IEqualityComparer _comparer; + + public TypeSymbolDelegator(IEqualityComparer comparer) + { + _comparer = comparer; + } + + public bool Equals(TypeIdentifier? x, TypeIdentifier? y) + { + return ReferenceEquals(x, y) || x switch + { + null => false, + _ => y is not null && _comparer.Equals(x.TypeSymbol, y.TypeSymbol) + }; + } + + public int GetHashCode(TypeIdentifier obj) + { + return _comparer.GetHashCode(obj.TypeSymbol); + } + } } public sealed record UnknownType(ITypeSymbol TypeSymbol) : TypeIdentifier(TypeSymbol); From 455b3a263a60a23de81abbab26e8278a4f3d06bd Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Sat, 19 Apr 2025 16:16:55 +0200 Subject: [PATCH 28/31] Reformat & CleanUp --- .../Extensions/CompilationExtensions.cs | 7 +- .../Extensions/EnumerableExtensions.cs | 15 ++-- .../Extensions/NotNullEvaluationExtensions.cs | 47 ++++++----- .../Extensions/StringExtensions.cs | 24 +++--- .../Extensions/TypeExtensions.cs | 82 +++++++++---------- 5 files changed, 87 insertions(+), 88 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Extensions/CompilationExtensions.cs b/src/DynamoDBGenerator.SourceGenerator/Extensions/CompilationExtensions.cs index b0a31f55..32e858bf 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Extensions/CompilationExtensions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Extensions/CompilationExtensions.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace DynamoDBGenerator.SourceGenerator.Extensions; // Great source: https://github.com/dotnet/runtime/blob/main/src/tools/illink/src/ILLink.RoslynAnalyzer/CompilationExtensions.cs public static class CompilationExtensions { - public static ReadOnlySpan GetTypeSymbols(this Compilation compilation, ImmutableArray classDeclarations) + public static ReadOnlySpan GetTypeSymbols(this Compilation compilation, + ImmutableArray classDeclarations) { var span = classDeclarations.AsSpan(); var symbols = new INamedTypeSymbol[classDeclarations.Length]; @@ -19,7 +19,8 @@ public static ReadOnlySpan GetTypeSymbols(this Compilation com .GetSemanticModel(classDeclarationSyntax.SyntaxTree) .GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol typeSymbol) - throw new ArgumentException($"Could not convert the '{classDeclarationSyntax.ToFullString()}' into a '{nameof(ITypeSymbol)}'."); + throw new ArgumentException( + $"Could not convert the '{classDeclarationSyntax.ToFullString()}' into a '{nameof(ITypeSymbol)}'."); symbols[i] = typeSymbol; } diff --git a/src/DynamoDBGenerator.SourceGenerator/Extensions/EnumerableExtensions.cs b/src/DynamoDBGenerator.SourceGenerator/Extensions/EnumerableExtensions.cs index a1a59254..ba772735 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Extensions/EnumerableExtensions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Extensions/EnumerableExtensions.cs @@ -16,12 +16,11 @@ public static IEnumerable AllButLast(this IEnumerable enumerable, Func< buffer = item; } - if (isBuffered) - { - yield return buffer!; - } + if (isBuffered) yield return buffer!; } - public static IEnumerable AllAndLast(this IEnumerable enumerable, Func @default, Func onLast) + + public static IEnumerable AllAndLast(this IEnumerable enumerable, Func @default, + Func onLast) { var buffer = default(T); var isBuffered = false; @@ -35,10 +34,6 @@ public static IEnumerable AllAndLast(this IEnumerable en buffer = item; } - if (isBuffered) - { - yield return onLast(buffer!); - } + if (isBuffered) yield return onLast(buffer!); } - } \ No newline at end of file diff --git a/src/DynamoDBGenerator.SourceGenerator/Extensions/NotNullEvaluationExtensions.cs b/src/DynamoDBGenerator.SourceGenerator/Extensions/NotNullEvaluationExtensions.cs index 28bc64a2..9de3e02d 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Extensions/NotNullEvaluationExtensions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Extensions/NotNullEvaluationExtensions.cs @@ -1,9 +1,11 @@ using Microsoft.CodeAnalysis; + namespace DynamoDBGenerator.SourceGenerator.Extensions; public static class NotNullEvaluationExtensions { - public static string NotNullTernaryExpression(this ITypeSymbol typeSymbol, in string accessPattern, in string truthy, + public static string NotNullTernaryExpression(this ITypeSymbol typeSymbol, in string accessPattern, + in string truthy, in string falsy) { var result = Expression(in typeSymbol, in accessPattern) is { } expression @@ -18,47 +20,51 @@ private static string CreateException(in string accessPattern) return $"throw {Constants.DynamoDBGenerator.ExceptionHelper.NullExceptionMethod}(nameof({accessPattern}));"; } - public static IEnumerable NotNullIfStatement(this ITypeSymbol typeSymbol, string accessPattern, string truthy) + public static IEnumerable NotNullIfStatement(this ITypeSymbol typeSymbol, string accessPattern, + string truthy) { return NotNullIfStatement(typeSymbol, accessPattern, obj: truthy); } - public static IEnumerable NotNullIfStatement(this ITypeSymbol typeSymbol, string accessPattern, IEnumerable truthy) + + public static IEnumerable NotNullIfStatement(this ITypeSymbol typeSymbol, string accessPattern, + IEnumerable truthy) { return NotNullIfStatement(typeSymbol, accessPattern, obj: truthy); } - + private static IEnumerable NotNullIfStatement(this ITypeSymbol typeSymbol, string accessPattern, object obj) { if (Expression(typeSymbol, accessPattern) is not { } expression) { - if(obj is string single) - yield return single; - else if(obj is IEnumerable truthies) - foreach (var x in truthies) - yield return x; - else - throw new NotImplementedException($"Method '{nameof(NotNullIfStatement)}' could not determine type '{obj.GetType().Name}'"); + if (obj is string single) + yield return single; + else if (obj is IEnumerable truthies) + foreach (var x in truthies) + yield return x; + else + throw new NotImplementedException( + $"Method '{nameof(NotNullIfStatement)}' could not determine type '{obj.GetType().Name}'"); } else { - - var ifClause = obj switch + var ifClause = obj switch { - string single => $"if ({expression})".CreateScope(single), - IEnumerable multiple => $"if ({expression})".CreateScope(multiple), - _ => throw new NotImplementedException($"Method '{nameof(NotNullIfStatement)}' could not determine type '{obj.GetType().Name}'") + string single => $"if ({expression})".CreateScope(single), + IEnumerable multiple => $"if ({expression})".CreateScope(multiple), + _ => throw new NotImplementedException( + $"Method '{nameof(NotNullIfStatement)}' could not determine type '{obj.GetType().Name}'") }; var enumerable = typeSymbol.NullableAnnotation switch { NullableAnnotation.None or NullableAnnotation.Annotated => ifClause, - NullableAnnotation.NotAnnotated => ifClause.Concat("else".CreateScope(CreateException(in accessPattern))), + NullableAnnotation.NotAnnotated => ifClause.Concat( + "else".CreateScope(CreateException(in accessPattern))), _ => throw new ArgumentOutOfRangeException(typeSymbol.ToDisplayString()) }; foreach (var element in enumerable) yield return element; } - } @@ -73,7 +79,6 @@ private static IEnumerable NotNullIfStatement(this ITypeSymbol typeSymbo static string? OnValueType(in ITypeSymbol typeSymbol, in string accessPattern) { - if (typeSymbol.TryGetNullableValueType() is not { } namedTypeSymbol) return null; @@ -86,6 +91,4 @@ private static IEnumerable NotNullIfStatement(this ITypeSymbol typeSymbo : $"{accessPattern} is not null && {expression}"; } } - - -} +} \ No newline at end of file diff --git a/src/DynamoDBGenerator.SourceGenerator/Extensions/StringExtensions.cs b/src/DynamoDBGenerator.SourceGenerator/Extensions/StringExtensions.cs index 8685a1f9..913bdbf2 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Extensions/StringExtensions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Extensions/StringExtensions.cs @@ -1,18 +1,21 @@ +using System.Runtime.CompilerServices; + namespace DynamoDBGenerator.SourceGenerator.Extensions; public static class StringExtensions { - public static string ToCamelCaseFromPascal(this string str, [System.Runtime.CompilerServices.CallerMemberName] string? memberName = null) + public static string ToCamelCaseFromPascal(this string str, [CallerMemberName] string? memberName = null) { return ToCamelCaseFromPascal(str.AsSpan(), memberName).ToString(); } - public static string ToPrivateFieldFromPascal(this string str, [System.Runtime.CompilerServices.CallerMemberName] string? memberName = null) + public static string ToPrivateFieldFromPascal(this string str, [CallerMemberName] string? memberName = null) { return ToPrivateFieldFromPascal(str.AsSpan(), memberName).ToString(); } - public static ReadOnlySpan ToPrivateFieldFromPascal(this ReadOnlySpan span, [System.Runtime.CompilerServices.CallerMemberName] string? memberName = null) + public static ReadOnlySpan ToPrivateFieldFromPascal(this ReadOnlySpan span, + [CallerMemberName] string? memberName = null) { if (span.Length is 0) throw new ArgumentException($"Null or Empty string was provided from '{memberName}'"); @@ -20,7 +23,7 @@ public static ReadOnlySpan ToPrivateFieldFromPascal(this ReadOnlySpan ToPrivateFieldFromPascal(this ReadOnlySpan ToCamelCaseFromPascal(this ReadOnlySpan span, [System.Runtime.CompilerServices.CallerMemberName] string? memberName = null) + + public static ReadOnlySpan ToCamelCaseFromPascal(this ReadOnlySpan span, + [CallerMemberName] string? memberName = null) { if (span.Length is 0) throw new ArgumentException($"Null or Empty string was provided from '{memberName}'"); @@ -38,7 +43,7 @@ public static ReadOnlySpan ToCamelCaseFromPascal(this ReadOnlySpan s var array = new char[span.Length]; - array[0] = Char.ToLowerInvariant(span[0]); + array[0] = char.ToLowerInvariant(span[0]); // Skip first element since we handled it manually. for (var i = 1; i < span.Length; i++) @@ -92,12 +97,9 @@ public static string ToAlphaNumericMethodName(this string txt) for (var i = 0; i < txt.Length; i++) { var c = txt[i]; - if (char.IsLetter(c) || index > 0 && char.IsNumber(c)) - { - arr[index++] = c; - } + if (char.IsLetter(c) || (index > 0 && char.IsNumber(c))) arr[index++] = c; } return new string(arr, 0, index); } -} +} \ No newline at end of file diff --git a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs index dc5ff5e7..d9898253 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Extensions/TypeExtensions.cs @@ -6,6 +6,13 @@ namespace DynamoDBGenerator.SourceGenerator.Extensions; public static class TypeExtensions { + private static readonly Dictionary TypeIdentifierDictionary = + new(SymbolEqualityComparer.IncludeNullability); + + private static readonly SymbolDisplayFormat DisplayFormat = new( + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters + ); + public static IEnumerable NamespaceDeclaration(this INamedTypeSymbol type, IEnumerable content) { return type.ContainingNamespace.IsGlobalNamespace @@ -48,10 +55,6 @@ public static Func CacheFactory(IEqualityComparer co return x => cache.TryGetValue(x, out var value) ? value : cache[x] = selector(x); } - - private static readonly Dictionary TypeIdentifierDictionary = - new(SymbolEqualityComparer.IncludeNullability); - public static TypeIdentifier TypeIdentifier(this ITypeSymbol type) { if (TypeIdentifierDictionary.TryGetValue(type, out var typeIdentifier)) @@ -71,43 +74,12 @@ static TypeIdentifier Create(ITypeSymbol typeSymbol) } } - - - //Source: https://referencesource.microsoft.com/#mscorlib/system/tuple.cs,49b112811bc359fd,references - private class TupleComparer : IEqualityComparer<(ITypeSymbol, ITypeSymbol)> - { - private readonly IEqualityComparer _comparer; - - public TupleComparer(IEqualityComparer comparer) - { - _comparer = comparer; - } - - public bool Equals((ITypeSymbol, ITypeSymbol) x, (ITypeSymbol, ITypeSymbol) y) - { - return _comparer.Equals(x.Item1, y.Item1) && _comparer.Equals(x.Item2, y.Item2); - } - - public int GetHashCode((ITypeSymbol, ITypeSymbol) obj) - { - var hashCode1 = obj.Item1 is null ? 0 : _comparer.GetHashCode(obj.Item1); - var hashCode2 = obj.Item2 is null ? 0 : _comparer.GetHashCode(obj.Item2); - return CombineHashCodes(hashCode1, hashCode2); - } - - private static int CombineHashCodes(int h1, int h2) - { - return ((h1 << 5) + h1) ^ h2; - } - } - public static Func SuffixedFullyQualifiedTypeName(string suffix, IEqualityComparer comparer) { IEqualityComparer<(ITypeSymbol, ITypeSymbol)> tupleComparer = new TupleComparer(comparer); var dict = new ConcurrentDictionary<(ITypeSymbol, ITypeSymbol), string>(tupleComparer); if (Equals(comparer, SymbolEqualityComparer.Default)) - { return (x, y) => dict.GetOrAdd( (x, y), tuple => @@ -117,17 +89,12 @@ public static Func SuffixedFullyQualifiedTypeN ? $"{p.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}.{c.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)}_{c.BaseType!.ToDisplayString()}{suffix}" : $"{p.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}.{c.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat).ToAlphaNumericMethodName()}{suffix}"; }); - } throw new NotSupportedException( $"Method {nameof(SuffixedFullyQualifiedTypeName)} does not implement equality comparer '{comparer.GetType().Name}" ); } - private static readonly SymbolDisplayFormat DisplayFormat = new( - genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters - ); - public static Func SuffixedTypeSymbolNameFactory(string? suffix, IEqualityComparer comparer) { @@ -201,8 +168,10 @@ when string.Join("_", namedTypeSymbol.TypeArguments.Select(NullableAnnotation)) _ => throw new NotImplementedException(ExceptionMessage(x)) }; - static string ExceptionMessage(ITypeSymbol typeSymbol) => - $"Could not apply naming suffix on type: {typeSymbol.ToDisplayString()}"; + static string ExceptionMessage(ITypeSymbol typeSymbol) + { + return $"Could not apply naming suffix on type: {typeSymbol.ToDisplayString()}"; + } } } @@ -282,4 +251,33 @@ static IEnumerable Iterator(ITypeSymbol typeSymbol) } while (namedTypeSymbol is { SpecialType: not SpecialType.System_Object }); } } + + + //Source: https://referencesource.microsoft.com/#mscorlib/system/tuple.cs,49b112811bc359fd,references + private class TupleComparer : IEqualityComparer<(ITypeSymbol, ITypeSymbol)> + { + private readonly IEqualityComparer _comparer; + + public TupleComparer(IEqualityComparer comparer) + { + _comparer = comparer; + } + + public bool Equals((ITypeSymbol, ITypeSymbol) x, (ITypeSymbol, ITypeSymbol) y) + { + return _comparer.Equals(x.Item1, y.Item1) && _comparer.Equals(x.Item2, y.Item2); + } + + public int GetHashCode((ITypeSymbol, ITypeSymbol) obj) + { + var hashCode1 = obj.Item1 is null ? 0 : _comparer.GetHashCode(obj.Item1); + var hashCode2 = obj.Item2 is null ? 0 : _comparer.GetHashCode(obj.Item2); + return CombineHashCodes(hashCode1, hashCode2); + } + + private static int CombineHashCodes(int h1, int h2) + { + return ((h1 << 5) + h1) ^ h2; + } + } } \ No newline at end of file From 4d1497d7aff5dea7155a057688c9cb6c3e64503e Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Sun, 20 Apr 2025 09:25:50 +0200 Subject: [PATCH 29/31] Reuse IsUnknown --- .../Generations/Marshalling/Marshaller.cs | 9 +++------ .../Generations/UnMarshaller.cs | 9 ++++----- .../Types/MarshallerOptions.cs | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs index bd8da44c..e3424195 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/Marshalling/Marshaller.cs @@ -209,12 +209,9 @@ internal static string InvokeMarshallerMethod(TypeIdentifier typeIdentifier, str { var invocation = $"{ClassName}.{GetSerializationMethodName(typeIdentifier.TypeSymbol)}({parameterReference}, {optionParam}, {dataMember})"; - if (options.IsConvertable(typeIdentifier.TypeSymbol)) - return invocation; - - return typeIdentifier is UnknownType - ? $"{AttributeValueUtilityFactory.ToAttributeValue}({invocation})" - : invocation; + return options.IsUnknown(typeIdentifier) is false + ? invocation + : $"{AttributeValueUtilityFactory.ToAttributeValue}({invocation})"; } internal static IEnumerable RootSignature(TypeIdentifier typeIdentifier) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs index 80cca395..da22b621 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs @@ -225,12 +225,11 @@ private static IEnumerable CreateTypeContents(IEnumerable ObjectAssignmentBlock(bool useParentheses, IEnumerable assignments, bool applySemiColon) diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs b/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs index 7b3bff78..0d5fc09d 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs @@ -18,7 +18,7 @@ public readonly struct MarshallerOptions public string FieldDeclaration { get; } public bool IsUnknown(TypeIdentifier typeSymbol) => - typeSymbol is UnknownType && !IsConvertable(typeSymbol.TypeSymbol); + typeSymbol is UnknownType && IsConvertable(typeSymbol.TypeSymbol) is false; private MarshallerOptions( From f3bae89f992ba117454b3aee13b7edf23bfb8702 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Sun, 20 Apr 2025 09:31:35 +0200 Subject: [PATCH 30/31] Fix --- .../Generations/AttributeExpressionValue.cs | 33 +++++++++---------- .../Types/MarshallerOptions.cs | 9 ++--- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs index bc0f4c83..d202c0a0 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Generations/AttributeExpressionValue.cs @@ -63,7 +63,7 @@ MarshallerOptions options var yields = (typeIdentifier switch { - {IsNullable:true} => $"if ({param} is null)".CreateScope( + { IsNullable: true } => $"if ({param} is null)".CreateScope( $"yield return new ({self}.Value, {AttributeValueUtilityFactory.Null});", "yield break;"), { TypeSymbol.IsReferenceType: true } => $"if ({param} is null)".CreateScope( $"throw {ExceptionHelper.NullExceptionMethod}(\"{structName}\");"), @@ -129,30 +129,29 @@ internal static IEnumerable CreateClasses(DynamoDBMarshallerArguments[] private static CodeFactory CreateStruct(TypeIdentifier typeIdentifier, Func fn, MarshallerOptions options) { - var dataMembers = - options.IsConvertable(typeIdentifier.TypeSymbol) - ? Array - .Empty<(bool IsUnknown, DynamoDbDataMember DDB, string AttributeReference, string - AttributeInterfaceName)>() - : fn(typeIdentifier.TypeSymbol) - .Select(x => ( - IsUnknown: options.IsUnknown(x.DataMember.TypeIdentifier), - DDB: x, - AttributeReference: TypeName(x.DataMember.TypeIdentifier.TypeSymbol), - AttributeInterfaceName: - $"{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerInterface}<{x.DataMember.TypeIdentifier.AnnotatedString}>" - )) - .ToArray(); + var dataMembers = options.IsConvertable(typeIdentifier) + ? Array.Empty<(bool IsUnknown, DynamoDbDataMember DDB, string AttributeReference, string AttributeInterfaceName)>() + : fn(typeIdentifier.TypeSymbol) + .Select(x => ( + IsUnknown: options.IsUnknown(x.DataMember.TypeIdentifier), + DDB: x, + AttributeReference: TypeName(x.DataMember.TypeIdentifier.TypeSymbol), + AttributeInterfaceName: + $"{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerInterface}<{x.DataMember.TypeIdentifier.AnnotatedString}>" + )) + .ToArray(); var structName = TypeName(typeIdentifier.TypeSymbol); var interfaceName = $"{Constants.DynamoDBGenerator.Marshaller.AttributeExpressionValueTrackerInterface}<{typeIdentifier.AnnotatedString}>"; var @struct = - $"public readonly struct {structName} : {interfaceName}".CreateScope(TypeContents(typeIdentifier, dataMembers, + $"public readonly struct {structName} : {interfaceName}".CreateScope(TypeContents(typeIdentifier, + dataMembers, structName, interfaceName, options)); - return new CodeFactory(@struct, dataMembers.Where(x => x.IsUnknown).Select(x => x.DDB.DataMember.TypeIdentifier)); + return new CodeFactory(@struct, + dataMembers.Where(x => x.IsUnknown).Select(x => x.DDB.DataMember.TypeIdentifier)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs b/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs index 0d5fc09d..56fa0de7 100644 --- a/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs +++ b/src/DynamoDBGenerator.SourceGenerator/Types/MarshallerOptions.cs @@ -17,9 +17,8 @@ public readonly struct MarshallerOptions public string FullName { get; } public string FieldDeclaration { get; } - public bool IsUnknown(TypeIdentifier typeSymbol) => - typeSymbol is UnknownType && IsConvertable(typeSymbol.TypeSymbol) is false; - + public bool IsUnknown(TypeIdentifier typeIdentifier) => typeIdentifier is UnknownType && IsConvertable(typeIdentifier) is false; + public bool IsConvertable(TypeIdentifier typeIdentifier) => typeIdentifier.TypeSymbol.TypeKind is TypeKind.Enum || Converters.ContainsKey(typeIdentifier.TypeSymbol); private MarshallerOptions( INamedTypeSymbol originalType, @@ -90,10 +89,6 @@ or ConversionStrategy.UpperCase return null; } - public bool IsConvertable(ITypeSymbol typeSymbol) - { - return typeSymbol.TypeKind is TypeKind.Enum || Converters.ContainsKey(typeSymbol); - } private Dictionary> Converters { get; } From bfef66fa9a0badd62056ee00e07792f54e91eaf6 Mon Sep 17 00:00:00 2001 From: Robert Andersson Date: Sun, 20 Apr 2025 09:33:41 +0200 Subject: [PATCH 31/31] Update test dependencies --- .../DynamoDBGenerator.SourceGenerator.Tests.csproj | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/DynamoDBGenerator.SourceGenerator.Tests/DynamoDBGenerator.SourceGenerator.Tests.csproj b/tests/DynamoDBGenerator.SourceGenerator.Tests/DynamoDBGenerator.SourceGenerator.Tests.csproj index 6e76d5d7..7bf1d8cd 100644 --- a/tests/DynamoDBGenerator.SourceGenerator.Tests/DynamoDBGenerator.SourceGenerator.Tests.csproj +++ b/tests/DynamoDBGenerator.SourceGenerator.Tests/DynamoDBGenerator.SourceGenerator.Tests.csproj @@ -10,14 +10,10 @@ - + - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + +