Skip to content

Commit 3732196

Browse files
committed
Support [SuppressDefaultInitialization] with VersionTolerant, and add validation
1 parent 75bacf1 commit 3732196

6 files changed

Lines changed: 73 additions & 34 deletions

File tree

src/MemoryPack.Generator/DiagnosticDescriptors.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,12 @@ internal static class DiagnosticDescriptors
324324
category: Category,
325325
defaultSeverity: DiagnosticSeverity.Error,
326326
isEnabledByDefault: true);
327+
328+
public static readonly DiagnosticDescriptor SuppressDefaultInitializationMustBeSettable = new(
329+
id: "MEMPACK040",
330+
title: "Readonly member cannot specify [SuppressDefaultInitialization]",
331+
messageFormat: "The MemoryPackable object '{0}' member '{1}' has [SuppressDefaultInitialization], it cannot be readonly, init-only and required.",
332+
category: Category,
333+
defaultSeverity: DiagnosticSeverity.Error,
334+
isEnabledByDefault: true);
327335
}

src/MemoryPack.Generator/MemoryPackGenerator.Emitter.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ private string EmitDeserializeBody()
547547
{
548548
{{EmitDeserializeConstruction(" ")}}
549549
};
550-
{{Members.Select((x, i) => (x, i)).Where(v => v.x.SkipOverwriteByDefault && v.x.IsAssignable).Select(v => $" if ({v.i + 1} <= count) value.@{v.x.Name} = __{v.x.Name};").NewLine()}}
550+
{{EmitDeserializeConstructionWithBranching(" ")}}
551551
READ_END:
552552
{{readEndBody}}
553553
""";
@@ -917,10 +917,28 @@ string EmitDeserializeConstruction(string indent)
917917
{
918918
// all value is deserialized, __Name is exsits.
919919
return string.Join("," + Environment.NewLine, Members
920-
.Where(x => x is { IsSettable: true, IsConstructorParameter: false, SkipOverwriteByDefault: false })
920+
.Where(x => x is { IsSettable: true, IsConstructorParameter: false, SuppressDefaultInitialization: false })
921921
.Select(x => $"{indent}@{x.Name} = __{x.Name}"));
922922
}
923923

924+
string EmitDeserializeConstructionWithBranching(string indent)
925+
{
926+
var members = Members
927+
.Select((x, i) => (x, i))
928+
.Where(v => v.x.SuppressDefaultInitialization);
929+
930+
if (GenerateType is GenerateType.VersionTolerant or GenerateType.CircularReference)
931+
{
932+
return members
933+
.Select(v => $"{indent}if (deltas[{v.i}] != 0) value.@{v.x.Name} = __{v.x.Name};")
934+
.NewLine();
935+
}
936+
937+
return members
938+
.Select(v => $"{indent}if ({v.i + 1} <= count) value.@{v.x.Name} = __{v.x.Name};")
939+
.NewLine();
940+
}
941+
924942
string EmitUnionTemplate(IGeneratorContext context)
925943
{
926944
var classOrInterfaceOrRecord = IsRecord ? "record" : (Symbol.TypeKind == TypeKind.Interface) ? "interface" : "class";

src/MemoryPack.Generator/MemoryPackGenerator.Parser.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,11 @@ public bool Validate(TypeDeclarationSyntax syntax, IGeneratorContext context, bo
375375
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ReadOnlyFieldMustBeConstructorMember, item.GetLocation(syntax), Symbol.Name, item.Name));
376376
noError = false;
377377
}
378+
else if (item is { SuppressDefaultInitialization: true, IsAssignable: false })
379+
{
380+
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.SuppressDefaultInitializationMustBeSettable, item.GetLocation(syntax), Symbol.Name, item.Name));
381+
noError = false;
382+
}
378383
}
379384
}
380385

@@ -615,7 +620,7 @@ partial class MemberMeta
615620
public int Order { get; }
616621
public bool HasExplicitOrder { get; }
617622
public MemberKind Kind { get; }
618-
public bool SkipOverwriteByDefault { get; }
623+
public bool SuppressDefaultInitialization { get; }
619624

620625
MemberMeta(int order)
621626
{
@@ -631,7 +636,7 @@ public MemberMeta(ISymbol symbol, IMethodSymbol? constructor, ReferenceSymbols r
631636
this.Symbol = symbol;
632637
this.Name = symbol.Name;
633638
this.Order = sequentialOrder;
634-
this.SkipOverwriteByDefault = symbol.ContainsAttribute(references.SkipOverwriteDefaultAttribute);
639+
this.SuppressDefaultInitialization = symbol.ContainsAttribute(references.SkipOverwriteDefaultAttribute);
635640
var orderAttr = symbol.GetAttribute(references.MemoryPackOrderAttribute);
636641
if (orderAttr != null)
637642
{

tests/MemoryPack.Tests/DefaultValueTest.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,26 @@ namespace MemoryPack.Tests;
55
public class DefaultValueTest
66
{
77
[Fact]
8-
public void FieldDefaultValue()
8+
public void SuppressDefaultInitialization()
99
{
1010
var bin = MemoryPackSerializer.Serialize(new DefaultValuePlaceholder { X = 1 });
11-
var expected = new FieldDefaultValue();
12-
var deserializedValue = MemoryPackSerializer.Deserialize<FieldDefaultValue>(bin)!;
11+
var expected = new HasDefaultValue();
12+
var deserializedValue = MemoryPackSerializer.Deserialize<HasDefaultValue>(bin)!;
1313
deserializedValue.Y.Should().Be(default);
14-
deserializedValue.Z.Should().Be(expected.Z);
15-
deserializedValue.FromMethod.Should().Be(expected.FromMethod);
14+
deserializedValue.Z.Should().Be(default);
15+
deserializedValue.Y2.Should().Be(expected.Y2);
16+
deserializedValue.Z2.Should().Be(expected.Z2);
1617
}
1718

1819
[Fact]
19-
public void PropertyDefaultValue()
20+
public void SuppressDefaultInitialization_VersionTolerant()
2021
{
2122
var bin = MemoryPackSerializer.Serialize(new DefaultValuePlaceholder { X = 1 });
22-
var expected = new PropertyDefaultValue();
23-
var deserializedValue = MemoryPackSerializer.Deserialize<PropertyDefaultValue>(bin)!;
23+
var expected = new HasDefaultValueWithVersionTolerant();
24+
var deserializedValue = MemoryPackSerializer.Deserialize<HasDefaultValueWithVersionTolerant>(bin)!;
2425
deserializedValue.Y.Should().Be(default);
2526
deserializedValue.Z.Should().Be(expected.Z);
26-
deserializedValue.FromMethod.Should().Be(expected.FromMethod);
27+
deserializedValue.Y2.Should().Be(expected.Y2);
28+
deserializedValue.Z2.Should().Be(expected.Z2);
2729
}
2830
}

tests/MemoryPack.Tests/GeneratorDiagnosticsTest.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,22 @@ public partial class Tester
640640
public bool B1 { get; set; }
641641
}
642642
643+
""");
644+
}
645+
646+
[Fact]
647+
public void MEMPACK040_SuppressDefaultInitializationMustBeSettable()
648+
{
649+
Compile(40, """
650+
using MemoryPack;
651+
652+
[MemoryPackable]
653+
public partial class Tester
654+
{
655+
[SuppressDefaultInitialization]
656+
public required int I1 { get; init; }
657+
}
658+
643659
""");
644660
}
645661
}

tests/MemoryPack.Tests/Models/DefaultValues.cs

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,32 @@ partial class DefaultValuePlaceholder
66
public int X { get; set; }
77
}
88

9-
static class StaticMethod
10-
{
11-
public static int GetNumberCalls;
12-
13-
public static int GetNumber()
14-
{
15-
GetNumberCalls++;
16-
return 100;
17-
}
18-
}
19-
20-
[MemoryPackable]
21-
partial class FieldDefaultValue
9+
[MemoryPackable(GenerateType.VersionTolerant, SerializeLayout.Sequential)]
10+
partial class HasDefaultValueWithVersionTolerant
2211
{
2312
public int X;
13+
2414
public int Y = 12345;
15+
public float Z { get; set; } = 678.9f;
2516

2617
[SuppressDefaultInitialization]
27-
public float Z = 678.9f;
18+
public int Y2 = 12345;
2819

2920
[SuppressDefaultInitialization]
30-
public int FromMethod = StaticMethod.GetNumber();
21+
public float Z2 { get; set; } = 678.9f;
3122
}
3223

3324
[MemoryPackable]
34-
partial class PropertyDefaultValue
25+
partial class HasDefaultValue
3526
{
36-
public int X { get; init; }
37-
public int Y { get; set; } = 12345;
27+
public int X;
3828

39-
[SuppressDefaultInitialization]
29+
public int Y = 12345;
4030
public float Z { get; set; } = 678.9f;
4131

4232
[SuppressDefaultInitialization]
43-
public float W { get; init; } = 678.9f;
33+
public int Y2 = 12345;
4434

4535
[SuppressDefaultInitialization]
46-
public int FromMethod { get; set; } = StaticMethod.GetNumber();
36+
public float Z2 { get; set; } = 678.9f;
4737
}

0 commit comments

Comments
 (0)