Skip to content

Commit a42cbcb

Browse files
authored
Merge pull request #333 from pixsperdavid/main
Added support for nested classes
2 parents 1d361dc + f45bb22 commit a42cbcb

File tree

5 files changed

+95
-27
lines changed

5 files changed

+95
-27
lines changed

src/MemoryPack.Generator/DiagnosticDescriptors.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,6 @@ internal static class DiagnosticDescriptors
1717
defaultSeverity: DiagnosticSeverity.Error,
1818
isEnabledByDefault: true);
1919

20-
public static readonly DiagnosticDescriptor NestedNotAllow = new(
21-
id: "MEMPACK002",
22-
title: "MemoryPackable object must not be nested type",
23-
messageFormat: "The MemoryPackable object '{0}' must be not nested type",
24-
category: Category,
25-
defaultSeverity: DiagnosticSeverity.Error,
26-
isEnabledByDefault: true);
27-
2820
public static readonly DiagnosticDescriptor AbstractMustUnion = new(
2921
id: "MEMPACK003",
3022
title: "abstract/interface type of MemoryPackable object must annotate with Union",
@@ -340,4 +332,12 @@ internal static class DiagnosticDescriptors
340332
category: Category,
341333
defaultSeverity: DiagnosticSeverity.Error,
342334
isEnabledByDefault: true);
335+
336+
public static readonly DiagnosticDescriptor NestedContainingTypesMustBePartial = new(
337+
id: "MEMPACK042",
338+
title: "Nested MemoryPackable object's containing type(s) must be partial",
339+
messageFormat: "The MemoryPackable object '{0}' containing type(s) must be partial",
340+
category: Category,
341+
defaultSeverity: DiagnosticSeverity.Error,
342+
isEnabledByDefault: true);
343343
}

src/MemoryPack.Generator/MemoryPackGenerator.Emitter.cs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@ static void Generate(TypeDeclarationSyntax syntax, Compilation compilation, stri
2525
return;
2626
}
2727

28-
// nested is not allowed
29-
if (IsNested(syntax))
28+
if (IsNested(syntax) && !IsNestedContainingTypesPartial(syntax))
3029
{
31-
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.NestedNotAllow, syntax.Identifier.GetLocation(), typeSymbol.Name));
30+
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.NestedContainingTypesMustBePartial, syntax.Identifier.GetLocation(), typeSymbol.Name));
3231
return;
3332
}
3433

@@ -157,6 +156,21 @@ static bool IsPartial(TypeDeclarationSyntax typeDeclaration)
157156
return typeDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));
158157
}
159158

159+
static bool IsNestedContainingTypesPartial(TypeDeclarationSyntax typeDeclaration)
160+
{
161+
if (typeDeclaration.Parent is TypeDeclarationSyntax parentTypeDeclaration)
162+
{
163+
if (!IsPartial(parentTypeDeclaration))
164+
return false;
165+
166+
return IsNestedContainingTypesPartial(parentTypeDeclaration);
167+
}
168+
else
169+
{
170+
return true;
171+
}
172+
}
173+
160174
static bool IsNested(TypeDeclarationSyntax typeDeclaration)
161175
{
162176
return typeDeclaration.Parent is TypeDeclarationSyntax;
@@ -296,6 +310,21 @@ public void Emit(StringBuilder writer, IGeneratorContext context)
296310
(false, false) => "class",
297311
};
298312

313+
var containingTypeDeclarations = new List<string>();
314+
var containingType = Symbol.ContainingType;
315+
while (containingType is not null)
316+
{
317+
containingTypeDeclarations.Add((containingType.IsRecord, containingType.IsValueType) switch
318+
{
319+
(true, true) => $"partial record struct {containingType.Name}",
320+
(true, false) => $"partial record {containingType.Name}",
321+
(false, true) => $"partial struct {containingType.Name}",
322+
(false, false) => $"partial class {containingType.Name}",
323+
});
324+
containingType = containingType.ContainingType;
325+
}
326+
containingTypeDeclarations.Reverse();
327+
299328
var nullable = IsValueType ? "" : "?";
300329

301330
string staticRegisterFormatterMethod, staticMemoryPackableMethod, scopedRef, constraint, registerBody, registerT;
@@ -345,6 +374,12 @@ public void Emit(StringBuilder writer, IGeneratorContext context)
345374
? "Serialize(ref MemoryPackWriter"
346375
: "Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter>";
347376

377+
foreach (var declaration in containingTypeDeclarations)
378+
{
379+
writer.AppendLine(declaration);
380+
writer.AppendLine("{");
381+
}
382+
348383
writer.AppendLine($$"""
349384
partial {{classOrStructOrRecord}} {{TypeName}} : IMemoryPackable<{{TypeName}}>{{fixedSizeInterface}}
350385
{
@@ -420,6 +455,10 @@ public override void Deserialize(ref MemoryPackReader reader, {{scopedRef}} {{Ty
420455
writer.AppendLine(code);
421456
}
422457

458+
for(int i = 0; i < containingTypeDeclarations.Count; ++i)
459+
{
460+
writer.AppendLine("}");
461+
}
423462
}
424463

425464
private string EmitDeserializeBody()

tests/MemoryPack.Tests/GeneratorDiagnosticsTest.cs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,6 @@ public class Hoge
4343
""");
4444
}
4545

46-
[Fact]
47-
public void MEMPACK002_NestedNotAllow()
48-
{
49-
Compile(2, """
50-
using MemoryPack;
51-
52-
public partial class Hoge
53-
{
54-
[MemoryPackable]
55-
public partial class Huga
56-
{
57-
}
58-
}
59-
""");
60-
}
61-
6246
[Fact]
6347
public void MEMPACK003_AbstractMustUnion()
6448
{
@@ -703,6 +687,23 @@ public partial struct Tester
703687
}
704688
""");
705689
}
690+
691+
[Fact]
692+
public void MEMPACK042_NestedContainingTypesMustBePartial()
693+
{
694+
Compile(42, """
695+
using MemoryPack;
696+
697+
public struct NestedContainer
698+
{
699+
[MemoryPackable]
700+
public partial struct NestedStruct
701+
{
702+
public int I1 { get; init; }
703+
}
704+
}
705+
""");
706+
}
706707
}
707708

708709
#endif

tests/MemoryPack.Tests/GeneratorTest.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ public void Standard()
5050
VerifyEquivalent(new GlobalNamespaceType() { MyProperty = 10000 });
5151
}
5252

53+
[Fact]
54+
public void Nested()
55+
{
56+
VerifyEquivalent(new NestedContainer.StandardTypeNested() { One = 9999 });
57+
VerifyEquivalent(new DoublyNestedContainer.DoublyNestedContainerInner.StandardTypeDoublyNested() { One = 9999 });
58+
}
59+
5360
[Fact]
5461
public void Null()
5562
{

tests/MemoryPack.Tests/Models/StandardType.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,27 @@ public StandardStruct()
6262
}
6363
}
6464

65+
public partial class NestedContainer
66+
{
67+
[MemoryPackable]
68+
public partial class StandardTypeNested
69+
{
70+
public int One { get; set; }
71+
}
72+
}
73+
74+
public partial class DoublyNestedContainer
75+
{
76+
public partial class DoublyNestedContainerInner
77+
{
78+
[MemoryPackable]
79+
public partial class StandardTypeDoublyNested
80+
{
81+
public int One { get; set; }
82+
}
83+
}
84+
}
85+
6586

6687
[MemoryPackable]
6788
public partial class WithArray

0 commit comments

Comments
 (0)