Skip to content

Commit 285ac2a

Browse files
authored
Merge pull request #242 from Cysharp/hadashiA/default-value
Use default value from member declaration
2 parents e72d0d3 + 30c187c commit 285ac2a

5 files changed

Lines changed: 144 additions & 6 deletions

File tree

src/MemoryPack.Generator/Extensions.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,15 +251,14 @@ private static IEnumerable<TSource> DistinctByIterator<TSource, TKey>(IEnumerabl
251251
}
252252
}
253253

254-
public static bool TryGetConstructorParameter(this IMethodSymbol constructor, ISymbol member, out string? constructorParameterName)
254+
public static bool TryGetConstructorParameter(this IMethodSymbol constructor, ISymbol member, out IParameterSymbol? constructorParameter)
255255
{
256-
var constructorParameter = GetConstructorParameter(constructor, member.Name);
256+
constructorParameter = GetConstructorParameter(constructor, member.Name);
257257
if (constructorParameter == null && member.Name.StartsWith(UnderScorePrefix))
258258
{
259259
constructorParameter = GetConstructorParameter(constructor, member.Name.Substring(UnderScorePrefix.Length));
260260
}
261261

262-
constructorParameterName = constructorParameter?.Name;
263262
return constructorParameter != null;
264263

265264
static IParameterSymbol? GetConstructorParameter(IMethodSymbol constructor, string name) => constructor.Parameters.FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));

src/MemoryPack.Generator/MemoryPackGenerator.Emitter.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ private string EmitDeserializeBody()
516516
{
517517
{{(IsValueType ? "" : "if (value == null)")}}
518518
{
519-
{{Members.Where(x => x.Symbol != null).Select(x => $" __{x.Name} = default!;").NewLine()}}
519+
{{Members.Where(x => x.Symbol != null).Select(x => $" __{x.Name} = {x.DefaultValueExpression};").NewLine()}}
520520
}
521521
{{(IsValueType ? "#if false" : " else")}}
522522
{
@@ -1366,5 +1366,22 @@ public string EmitReadRefDeserialize(int i, bool requireDeltaCheck)
13661366
return $"{pre}reader.ReadValue(ref __{Name});";
13671367
}
13681368
}
1369+
1370+
string EmitConstantValue(object? constantValue)
1371+
{
1372+
if (constantValue != null)
1373+
{
1374+
return constantValue switch
1375+
{
1376+
string x => $"\"{x}\"",
1377+
char x => $"'{x}'",
1378+
float x => $"{x}f",
1379+
decimal x => $"{x}D",
1380+
bool x => x ? "true" : "false",
1381+
_ => constantValue.ToString()
1382+
};
1383+
}
1384+
return "null";
1385+
}
13691386
}
13701387

src/MemoryPack.Generator/MemoryPackGenerator.Parser.cs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,7 @@ partial class MemberMeta
615615
public int Order { get; }
616616
public bool HasExplicitOrder { get; }
617617
public MemberKind Kind { get; }
618+
public string DefaultValueExpression { get; } = "default!";
618619

619620
MemberMeta(int order)
620621
{
@@ -644,8 +645,12 @@ public MemberMeta(ISymbol symbol, IMethodSymbol? constructor, ReferenceSymbols r
644645

645646
if (constructor != null)
646647
{
647-
this.IsConstructorParameter = constructor.TryGetConstructorParameter(symbol, out var constructorParameterName);
648-
this.ConstructorParameterName = constructorParameterName;
648+
this.IsConstructorParameter = constructor.TryGetConstructorParameter(symbol, out var constructorParameter);
649+
this.ConstructorParameterName = constructorParameter?.Name;
650+
if (constructorParameter?.HasExplicitDefaultValue == true)
651+
{
652+
DefaultValueExpression = EmitConstantValue(constructorParameter.ExplicitDefaultValue);
653+
}
649654
}
650655
else
651656
{
@@ -664,6 +669,24 @@ public MemberMeta(ISymbol symbol, IMethodSymbol? constructor, ReferenceSymbols r
664669
;
665670
MemberType = f.Type;
666671

672+
// Detect default value
673+
foreach (var syntaxReference in f.DeclaringSyntaxReferences)
674+
{
675+
var syntax = syntaxReference.GetSyntax();
676+
if (syntax is FieldDeclarationSyntax { Declaration.Variables: { Count: > 0 } variables })
677+
{
678+
if (variables.First().Initializer is { } initializer)
679+
{
680+
DefaultValueExpression = initializer.Value.ToString();
681+
break;
682+
}
683+
}
684+
if (syntax is VariableDeclaratorSyntax { Initializer: { } initializer2 })
685+
{
686+
DefaultValueExpression = initializer2.Value.ToString();
687+
break;
688+
}
689+
}
667690
}
668691
else if (symbol is IPropertySymbol p)
669692
{
@@ -676,6 +699,16 @@ public MemberMeta(ISymbol symbol, IMethodSymbol? constructor, ReferenceSymbols r
676699
#endif
677700
&& (p.SetMethod != null && !p.SetMethod.IsInitOnly);
678701
MemberType = p.Type;
702+
703+
// Detect default value
704+
foreach (var syntaxReference in p.DeclaringSyntaxReferences)
705+
{
706+
if (syntaxReference.GetSyntax() is PropertyDeclarationSyntax { Initializer: { } initializer })
707+
{
708+
DefaultValueExpression = initializer.Value.ToString();
709+
break;
710+
}
711+
}
679712
}
680713
else
681714
{
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using MemoryPack.Tests.Models;
2+
3+
namespace MemoryPack.Tests;
4+
5+
public class DefaultValueTest
6+
{
7+
[Fact]
8+
public void FieldDefaultValue()
9+
{
10+
var bin = MemoryPackSerializer.Serialize(new DefaultValuePlaceholder { X = 1 });
11+
var expected = new FieldDefaultValue();
12+
var deserializedValue = MemoryPackSerializer.Deserialize<FieldDefaultValue>(bin)!;
13+
deserializedValue.Y.Should().Be(expected.Y);
14+
deserializedValue.Z.Should().Be(expected.Z);
15+
deserializedValue.S.Should().Be(expected.S);
16+
deserializedValue.B.Should().Be(expected.B);
17+
}
18+
19+
[Fact]
20+
public void PropertyDefaultValue()
21+
{
22+
var bin = MemoryPackSerializer.Serialize(new DefaultValuePlaceholder { X = 1 });
23+
var expected = new PropertyDefaultValue();
24+
var deserializedValue = MemoryPackSerializer.Deserialize<PropertyDefaultValue>(bin)!;
25+
deserializedValue.Y.Should().Be(expected.Y);
26+
deserializedValue.Z.Should().Be(expected.Z);
27+
deserializedValue.S.Should().Be(expected.S);
28+
deserializedValue.B.Should().Be(expected.B);
29+
}
30+
31+
[Fact]
32+
public void CtorParamDefaultValue()
33+
{
34+
var bin = MemoryPackSerializer.Serialize(new DefaultValuePlaceholder { X = 1 });
35+
var expected = new CtorParamDefaultValue(1);
36+
var deserializedValue = MemoryPackSerializer.Deserialize<CtorParamDefaultValue>(bin)!;
37+
deserializedValue.Y.Should().Be(expected.Y);
38+
deserializedValue.Z.Should().Be(expected.Z);
39+
deserializedValue.S.Should().Be(expected.S);
40+
deserializedValue.B.Should().Be(expected.B);
41+
}
42+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
namespace MemoryPack.Tests.Models;
2+
3+
[MemoryPackable]
4+
partial class DefaultValuePlaceholder
5+
{
6+
public int X { get; set; }
7+
}
8+
9+
[MemoryPackable]
10+
partial class FieldDefaultValue
11+
{
12+
public int X;
13+
public int Y = 12345;
14+
public float Z = 678.9f;
15+
public string S = "aaaaaaaaa";
16+
public bool B = true;
17+
}
18+
19+
[MemoryPackable]
20+
partial class PropertyDefaultValue
21+
{
22+
public int X { get; set; }
23+
public int Y { get; set; } = 12345;
24+
public float Z { get; set; } = 678.9f;
25+
public string S { get; set; } = "aaaaaaaaa";
26+
public bool B { get; set; } = true;
27+
}
28+
29+
[MemoryPackable]
30+
partial class CtorParamDefaultValue
31+
{
32+
public int X;
33+
public int Y;
34+
public float Z;
35+
public string S;
36+
public bool B;
37+
38+
[MemoryPackConstructor]
39+
public CtorParamDefaultValue(int x, int y = 12345, float z = 678.9f, string s = "aaaaaa", bool b = true)
40+
{
41+
X = x;
42+
Y = y;
43+
Z = z;
44+
S = s;
45+
B = b;
46+
}
47+
}

0 commit comments

Comments
 (0)