Skip to content

Commit 34a7e87

Browse files
committed
order impl complete
1 parent 5fd2d25 commit 34a7e87

File tree

8 files changed

+201
-34
lines changed

8 files changed

+201
-34
lines changed

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ All members must be memorypack-serializable, if not, code generator reports erro
126126

127127
![image](https://user-images.githubusercontent.com/46207/192413557-8a47d668-5339-46c5-a3da-a77841666f81.png)
128128

129-
MemoryPack has 24 diagnostics rules(`MEMPACK001` to `MEMPACK024`) to be define comfortably.
129+
MemoryPack has 24 diagnostics rules(`MEMPACK001` to `MEMPACK026`) to be define comfortably.
130130

131131
If target type is defined MemoryPack serialization externally and registered, use `[MemoryPackAllowSerialize]` to silent diagnostics.
132132

@@ -141,6 +141,20 @@ public partial class Sample2
141141

142142
Member order is **important**, MemoryPack does not serialize any member-name and other tags, serialize in the declared order. If the type is inherited, serialize in the order of parent → child. Member orders can not change for the deserialization. For the schema evolution, see [Version tolerant](#version-tolerant) section.
143143

144+
Default order is sequential but you can choose explicit layout with `[MemoryPackable(SerializeLayout.Explicit)]` and `[MemoryPackOrder()]`.
145+
146+
```csharp
147+
// serialize Prop0 -> Prop1
148+
[MemoryPackable(SerializeLayout.Explicit)]
149+
public partial class SampleExplicitOrder
150+
{
151+
[MemoryPackOrder(1)]
152+
public int Prop1 { get; set; }
153+
[MemoryPackOrder(0)]
154+
public int Prop0 { get; set; }
155+
}
156+
```
157+
144158
### Constructor selection
145159

146160
MemoryPack supports parameterized constructor not only parameterless constructor. The selection of the constructor follows these rules. Both class and struct follows same.

sandbox/SandboxConsoleApp/ForReadMe.cs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -140,22 +140,11 @@ public partial class Version2
140140
}
141141

142142

143-
144-
[MemoryPackable(GenerateType.Object)]
145-
public partial class Sonota1
146-
{
147-
148-
}
149-
150-
[MemoryPackable(SerializeLayout.Sequential)]
151-
public partial class Sonota2
152-
{
153-
154-
}
155-
156-
[MemoryPackable(GenerateType.Object, SerializeLayout.Explicit)]
157-
public partial class Sonota3
143+
[MemoryPackable(SerializeLayout.Explicit)]
144+
public partial class SampleExplicitOrder
158145
{
146+
[MemoryPackOrder(1)]
147+
public int Prop1 { get; set; }
159148
[MemoryPackOrder(0)]
160-
public int MyProperty { get; set; }
149+
public int Prop0 { get; set; }
161150
}

sandbox/SandboxConsoleApp/Program.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
using System.Text;
2121
using System.Xml.Linq;
2222

23+
var a = int.MaxValue;
24+
var b = ~a;
25+
Console.WriteLine(b);
2326

24-
Console.WriteLine("foo");
2527

2628
// ---
2729

@@ -43,6 +45,33 @@
4345

4446

4547

48+
[MemoryPackable(GenerateType.Object)]
49+
public partial class Sonota1
50+
{
51+
// public NoSerializableObject? MyProperty { get; set; }
52+
}
53+
54+
public class NoSerializableObject
55+
{
56+
57+
}
58+
59+
[MemoryPackable(SerializeLayout.Explicit)]
60+
public partial class Sonota2
61+
{
62+
[MemoryPackOrder(1)]
63+
public int MyProperty1 { get; set; }
64+
[MemoryPackOrder(0)]
65+
public int MyProperty2 { get; set; }
66+
}
67+
68+
[MemoryPackable(GenerateType.Object, SerializeLayout.Explicit)]
69+
public partial class Sonota3
70+
{
71+
[MemoryPackOrder(0)]
72+
public int MyProperty { get; set; }
73+
}
74+
4675

4776

4877

src/MemoryPack.Core/Attributes.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ public sealed class MemoryPackableAttribute : Attribute
88
public GenerateType GenerateType { get; }
99
public SerializeLayout SerializeLayout { get; }
1010

11+
// ctor parameter count is used in MemoryPackGenerator.Parser TypeMeta for detect which ctor used.
12+
// if modify ctor, be careful.
13+
1114
public MemoryPackableAttribute(GenerateType generateType = GenerateType.Object, SerializeLayout serializeLayout = SerializeLayout.Sequential)
1215
{
1316
this.GenerateType = generateType;
1417
this.SerializeLayout = serializeLayout;
1518
}
1619

20+
// set SerializeLayout only allows Object
1721
public MemoryPackableAttribute(SerializeLayout serializeLayout)
1822
{
1923
this.GenerateType = GenerateType.Object;

src/MemoryPack.Generator/GenerateType.cs renamed to src/MemoryPack.Generator/CoreEnums.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@ public enum GenerateType
77
Collection,
88
NoGenerate
99
}
10+
11+
public enum SerializeLayout
12+
{
13+
Sequential, // default
14+
Explicit
15+
}

src/MemoryPack.Generator/DiagnosticDescriptors.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,20 @@ internal static class DiagnosticDescriptors
204204
category: Category,
205205
defaultSeverity: DiagnosticSeverity.Error,
206206
isEnabledByDefault: true);
207+
208+
public static readonly DiagnosticDescriptor AllMembersMustAnnotateOrder = new(
209+
id: "MEMPACK025",
210+
title: "All members must annotate MemoryPackOrder when SerializeLayout.Explicit",
211+
messageFormat: "The MemoryPackable object '{0}' member '{1}' is not annotated MemoryPackOrder",
212+
category: Category,
213+
defaultSeverity: DiagnosticSeverity.Error,
214+
isEnabledByDefault: true);
215+
216+
public static readonly DiagnosticDescriptor AllMembersMustBeContinuousNumber = new(
217+
id: "MEMPACK026",
218+
title: "All MemoryPackOrder members must be continuous number from zero",
219+
messageFormat: "The MemoryPackable object '{0}' member '{1}' is not continuous number from zero",
220+
category: Category,
221+
defaultSeverity: DiagnosticSeverity.Error,
222+
isEnabledByDefault: true);
207223
}

src/MemoryPack.Generator/MemoryPackGenerator.Parser.cs

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.CodeAnalysis;
22
using Microsoft.CodeAnalysis.CSharp;
33
using Microsoft.CodeAnalysis.CSharp.Syntax;
4+
using System.ComponentModel;
45
using System.Runtime.Serialization;
56
using System.Text;
67

@@ -37,6 +38,7 @@ partial class TypeMeta
3738
readonly ReferenceSymbols reference;
3839
public INamedTypeSymbol Symbol { get; }
3940
public GenerateType GenerateType { get; }
41+
public SerializeLayout SerializeLayout { get; }
4042
/// <summary>MinimallyQualifiedFormat(include generics T>)</summary>
4143
public string TypeName { get; }
4244
public MemberMeta[] Members { get; }
@@ -63,16 +65,24 @@ public TypeMeta(INamedTypeSymbol symbol, ReferenceSymbols reference)
6365
if (packableCtorArgs == null)
6466
{
6567
this.GenerateType = GenerateType.NoGenerate;
68+
this.SerializeLayout = SerializeLayout.Sequential;
6669
}
6770
else if (packableCtorArgs.Value.Length != 0)
6871
{
69-
// TODO: MemoryPackable has two attribtue
70-
// (SerializeLayout serializeLayout)
71-
// (GenerateType generateType = GenerateType.Object, SerializeLayout serializeLayout = SerializeLayout.Sequential)
72-
// so check length first and choose which constructor used.
73-
var ctorValue = packableCtorArgs.Value[0];
74-
var generateType = ctorValue.Value ?? GenerateType.Object;
75-
this.GenerateType = (GenerateType)generateType;
72+
// MemoryPackable has two attribtue
73+
if (packableCtorArgs.Value.Length == 1)
74+
{
75+
// (SerializeLayout serializeLayout)
76+
var ctorValue = packableCtorArgs.Value[0];
77+
this.SerializeLayout = (SerializeLayout)(ctorValue.Value ?? SerializeLayout.Sequential);
78+
this.GenerateType = GenerateType.Object;
79+
}
80+
else
81+
{
82+
// (GenerateType generateType = GenerateType.Object, SerializeLayout serializeLayout = SerializeLayout.Sequential)
83+
this.GenerateType = (GenerateType)(packableCtorArgs.Value[0].Value ?? GenerateType.Object);
84+
this.SerializeLayout = (SerializeLayout)(packableCtorArgs.Value[1].Value ?? SerializeLayout.Sequential);
85+
}
7686
}
7787

7888
this.TypeName = symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
@@ -101,8 +111,10 @@ public TypeMeta(INamedTypeSymbol symbol, ReferenceSymbols reference)
101111
}
102112
return true;
103113
})
104-
.Select(x => new MemberMeta(x, Constructor, reference))
114+
.Select((x, i) => new MemberMeta(x, Constructor, reference, i))
115+
.OrderBy(x => x.Order)
105116
.ToArray();
117+
106118
this.IsValueType = symbol.IsValueType;
107119
this.IsUnmanagedType = symbol.IsUnmanagedType;
108120
this.IsInterfaceOrAbstract = symbol.IsAbstract;
@@ -112,6 +124,7 @@ public TypeMeta(INamedTypeSymbol symbol, ReferenceSymbols reference)
112124
this.OnSerialized = CollectMethod(reference.MemoryPackOnSerializedAttribute, IsValueType);
113125
this.OnDeserializing = CollectMethod(reference.MemoryPackOnDeserializingAttribute, IsValueType);
114126
this.OnDeserialized = CollectMethod(reference.MemoryPackOnDeserializedAttribute, IsValueType);
127+
115128
if (IsUnion)
116129
{
117130
this.UnionTags = symbol.GetAttributes()
@@ -271,7 +284,9 @@ public bool Validate(TypeDeclarationSyntax syntax, SourceProductionContext conte
271284
var allParameterExists = this.Constructor.Parameters.All(x => nameDict.Contains(x.Name));
272285
if (!allParameterExists)
273286
{
274-
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ConstructorHasNoMatchedParameter, syntax.Identifier.GetLocation(), Symbol.Name));
287+
var location = Constructor.Locations.FirstOrDefault() ?? syntax.Identifier.GetLocation();
288+
289+
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ConstructorHasNoMatchedParameter, location, Symbol.Name));
275290
noError = false;
276291
}
277292
}
@@ -283,12 +298,12 @@ public bool Validate(TypeDeclarationSyntax syntax, SourceProductionContext conte
283298
{
284299
// diagnostics location should be method identifier
285300
// however methodsymbol -> methodsyntax is slightly hard so use type identifier instead.
286-
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.OnMethodHasParameter, syntax.Identifier.GetLocation(), Symbol.Name, item.Name));
301+
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.OnMethodHasParameter, item.GetLocation(syntax), Symbol.Name, item.Name));
287302
noError = false;
288303
}
289304
if (IsUnmanagedType)
290305
{
291-
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.OnMethodInUnamannagedType, syntax.Identifier.GetLocation(), Symbol.Name, item.Name));
306+
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.OnMethodInUnamannagedType, item.GetLocation(syntax), Symbol.Name, item.Name));
292307
noError = false;
293308
}
294309
}
@@ -304,8 +319,10 @@ public bool Validate(TypeDeclarationSyntax syntax, SourceProductionContext conte
304319
var ignore = item.ContainsAttribute(reference.MemoryPackIgnoreAttribute);
305320
if (include || ignore)
306321
{
322+
var location = item.Locations.FirstOrDefault() ?? syntax.Identifier.GetLocation();
323+
307324
var attr = include ? "MemoryPackInclude" : "MemoryPackIgnore";
308-
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.OverrideMemberCantAddAnnotation, syntax.Identifier.GetLocation(), Symbol.Name, item.Name, attr));
325+
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.OverrideMemberCantAddAnnotation, location, Symbol.Name, item.Name, attr));
309326
noError = false;
310327
}
311328
}
@@ -322,26 +339,57 @@ public bool Validate(TypeDeclarationSyntax syntax, SourceProductionContext conte
322339
// exists can't serialize member
323340
foreach (var item in Members)
324341
{
342+
325343
if (item.Kind == MemberKind.NonSerializable)
326344
{
327345
if (item.MemberType.SpecialType is SpecialType.System_Object or SpecialType.System_Array or SpecialType.System_Delegate or SpecialType.System_MulticastDelegate || item.MemberType.TypeKind == TypeKind.Delegate)
328346
{
329-
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MemberCantSerializeType, syntax.Identifier.GetLocation(), Symbol.Name, item.Name, item.MemberType.FullyQualifiedToString()));
347+
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MemberCantSerializeType, item.GetLocation(syntax), Symbol.Name, item.Name, item.MemberType.FullyQualifiedToString()));
330348
noError = false;
331349
}
332350
else
333351
{
334-
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MemberIsNotMemoryPackable, syntax.Identifier.GetLocation(), Symbol.Name, item.Name, item.MemberType.FullyQualifiedToString()));
352+
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MemberIsNotMemoryPackable, item.GetLocation(syntax), Symbol.Name, item.Name, item.MemberType.FullyQualifiedToString()));
335353
noError = false;
336354
}
337355
}
338356
else if (item.Kind == MemberKind.RefLike)
339357
{
340-
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MemberIsRefStruct, syntax.Identifier.GetLocation(), Symbol.Name, item.Name, item.MemberType.FullyQualifiedToString()));
358+
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MemberIsRefStruct, item.GetLocation(syntax), Symbol.Name, item.Name, item.MemberType.FullyQualifiedToString()));
341359
noError = false;
342360
}
343361
}
344362

363+
// order
364+
if (SerializeLayout == SerializeLayout.Explicit)
365+
{
366+
// All members must annotate MemoryPackOrder
367+
foreach (var item in Members)
368+
{
369+
if (!item.HasExplicitOrder)
370+
{
371+
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.AllMembersMustAnnotateOrder, item.GetLocation(syntax), Symbol.Name, item.Name));
372+
noError = false;
373+
}
374+
}
375+
376+
// Annotated MemoryPackOrder must be continuous number from zero.
377+
if (noError)
378+
{
379+
var expectedOrder = 0;
380+
foreach (var item in Members)
381+
{
382+
if (item.Order != expectedOrder)
383+
{
384+
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.AllMembersMustBeContinuousNumber, item.GetLocation(syntax), Symbol.Name, item.Name));
385+
noError = false;
386+
break;
387+
}
388+
expectedOrder++;
389+
}
390+
}
391+
}
392+
345393
// Union validations
346394
if (IsUnion)
347395
{
@@ -422,12 +470,26 @@ partial class MemberMeta
422470
public bool IsSettable { get; }
423471
public bool IsAssignable { get; }
424472
public bool IsConstructorParameter { get; }
473+
public int Order { get; }
474+
public bool HasExplicitOrder { get; }
425475
public MemberKind Kind { get; }
426476

427-
public MemberMeta(ISymbol symbol, IMethodSymbol? constructor, ReferenceSymbols references)
477+
public MemberMeta(ISymbol symbol, IMethodSymbol? constructor, ReferenceSymbols references, int sequentialOrder)
428478
{
429479
this.Symbol = symbol;
430480
this.Name = symbol.Name;
481+
this.Order = sequentialOrder;
482+
var orderAttr = symbol.GetAttribute(references.MemoryPackOrderAttribute);
483+
if (orderAttr != null)
484+
{
485+
this.Order = (int)(orderAttr.ConstructorArguments[0].Value ?? sequentialOrder);
486+
this.HasExplicitOrder = true;
487+
488+
}
489+
else
490+
{
491+
this.HasExplicitOrder = false;
492+
}
431493

432494
if (constructor != null)
433495
{
@@ -465,6 +527,12 @@ public MemberMeta(ISymbol symbol, IMethodSymbol? constructor, ReferenceSymbols r
465527
Kind = ParseMemberKind(symbol, MemberType, references);
466528
}
467529

530+
public Location GetLocation(TypeDeclarationSyntax fallback)
531+
{
532+
var location = Symbol.Locations.FirstOrDefault() ?? fallback.Identifier.GetLocation();
533+
return location;
534+
}
535+
468536
static MemberKind ParseMemberKind(ISymbol? memberSymbol, ITypeSymbol memberType, ReferenceSymbols references)
469537
{
470538
if (memberType.SpecialType is SpecialType.System_Object or SpecialType.System_Array or SpecialType.System_Delegate or SpecialType.System_MulticastDelegate || memberType.TypeKind == TypeKind.Delegate)
@@ -587,4 +655,10 @@ public MethodMeta(IMethodSymbol symbol, bool isValueType)
587655
this.IsStatic = symbol.IsStatic;
588656
this.IsValueType = isValueType;
589657
}
658+
659+
public Location GetLocation(TypeDeclarationSyntax fallback)
660+
{
661+
var location = Symbol.Locations.FirstOrDefault() ?? fallback.Identifier.GetLocation();
662+
return location;
663+
}
590664
}

0 commit comments

Comments
 (0)