Skip to content

Commit

Permalink
Merge pull request #242 from Cysharp/hadashiA/default-value
Browse files Browse the repository at this point in the history
Use default value from member declaration
  • Loading branch information
hadashiA authored Mar 14, 2024
2 parents e72d0d3 + 30c187c commit 285ac2a
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 6 deletions.
5 changes: 2 additions & 3 deletions src/MemoryPack.Generator/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,14 @@ private static IEnumerable<TSource> DistinctByIterator<TSource, TKey>(IEnumerabl
}
}

public static bool TryGetConstructorParameter(this IMethodSymbol constructor, ISymbol member, out string? constructorParameterName)
public static bool TryGetConstructorParameter(this IMethodSymbol constructor, ISymbol member, out IParameterSymbol? constructorParameter)
{
var constructorParameter = GetConstructorParameter(constructor, member.Name);
constructorParameter = GetConstructorParameter(constructor, member.Name);
if (constructorParameter == null && member.Name.StartsWith(UnderScorePrefix))
{
constructorParameter = GetConstructorParameter(constructor, member.Name.Substring(UnderScorePrefix.Length));
}

constructorParameterName = constructorParameter?.Name;
return constructorParameter != null;

static IParameterSymbol? GetConstructorParameter(IMethodSymbol constructor, string name) => constructor.Parameters.FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
Expand Down
19 changes: 18 additions & 1 deletion src/MemoryPack.Generator/MemoryPackGenerator.Emitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ private string EmitDeserializeBody()
{
{{(IsValueType ? "" : "if (value == null)")}}
{
{{Members.Where(x => x.Symbol != null).Select(x => $" __{x.Name} = default!;").NewLine()}}
{{Members.Where(x => x.Symbol != null).Select(x => $" __{x.Name} = {x.DefaultValueExpression};").NewLine()}}
}
{{(IsValueType ? "#if false" : " else")}}
{
Expand Down Expand Up @@ -1366,5 +1366,22 @@ public string EmitReadRefDeserialize(int i, bool requireDeltaCheck)
return $"{pre}reader.ReadValue(ref __{Name});";
}
}

string EmitConstantValue(object? constantValue)
{
if (constantValue != null)
{
return constantValue switch
{
string x => $"\"{x}\"",
char x => $"'{x}'",
float x => $"{x}f",
decimal x => $"{x}D",
bool x => x ? "true" : "false",
_ => constantValue.ToString()
};
}
return "null";
}
}

37 changes: 35 additions & 2 deletions src/MemoryPack.Generator/MemoryPackGenerator.Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ partial class MemberMeta
public int Order { get; }
public bool HasExplicitOrder { get; }
public MemberKind Kind { get; }
public string DefaultValueExpression { get; } = "default!";

MemberMeta(int order)
{
Expand Down Expand Up @@ -644,8 +645,12 @@ public MemberMeta(ISymbol symbol, IMethodSymbol? constructor, ReferenceSymbols r

if (constructor != null)
{
this.IsConstructorParameter = constructor.TryGetConstructorParameter(symbol, out var constructorParameterName);
this.ConstructorParameterName = constructorParameterName;
this.IsConstructorParameter = constructor.TryGetConstructorParameter(symbol, out var constructorParameter);
this.ConstructorParameterName = constructorParameter?.Name;
if (constructorParameter?.HasExplicitDefaultValue == true)
{
DefaultValueExpression = EmitConstantValue(constructorParameter.ExplicitDefaultValue);
}
}
else
{
Expand All @@ -664,6 +669,24 @@ public MemberMeta(ISymbol symbol, IMethodSymbol? constructor, ReferenceSymbols r
;
MemberType = f.Type;

// Detect default value
foreach (var syntaxReference in f.DeclaringSyntaxReferences)
{
var syntax = syntaxReference.GetSyntax();
if (syntax is FieldDeclarationSyntax { Declaration.Variables: { Count: > 0 } variables })
{
if (variables.First().Initializer is { } initializer)
{
DefaultValueExpression = initializer.Value.ToString();
break;
}
}
if (syntax is VariableDeclaratorSyntax { Initializer: { } initializer2 })
{
DefaultValueExpression = initializer2.Value.ToString();
break;
}
}
}
else if (symbol is IPropertySymbol p)
{
Expand All @@ -676,6 +699,16 @@ public MemberMeta(ISymbol symbol, IMethodSymbol? constructor, ReferenceSymbols r
#endif
&& (p.SetMethod != null && !p.SetMethod.IsInitOnly);
MemberType = p.Type;

// Detect default value
foreach (var syntaxReference in p.DeclaringSyntaxReferences)
{
if (syntaxReference.GetSyntax() is PropertyDeclarationSyntax { Initializer: { } initializer })
{
DefaultValueExpression = initializer.Value.ToString();
break;
}
}
}
else
{
Expand Down
42 changes: 42 additions & 0 deletions tests/MemoryPack.Tests/DefaultValueTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using MemoryPack.Tests.Models;

namespace MemoryPack.Tests;

public class DefaultValueTest
{
[Fact]
public void FieldDefaultValue()
{
var bin = MemoryPackSerializer.Serialize(new DefaultValuePlaceholder { X = 1 });
var expected = new FieldDefaultValue();
var deserializedValue = MemoryPackSerializer.Deserialize<FieldDefaultValue>(bin)!;
deserializedValue.Y.Should().Be(expected.Y);
deserializedValue.Z.Should().Be(expected.Z);
deserializedValue.S.Should().Be(expected.S);
deserializedValue.B.Should().Be(expected.B);
}

[Fact]
public void PropertyDefaultValue()
{
var bin = MemoryPackSerializer.Serialize(new DefaultValuePlaceholder { X = 1 });
var expected = new PropertyDefaultValue();
var deserializedValue = MemoryPackSerializer.Deserialize<PropertyDefaultValue>(bin)!;
deserializedValue.Y.Should().Be(expected.Y);
deserializedValue.Z.Should().Be(expected.Z);
deserializedValue.S.Should().Be(expected.S);
deserializedValue.B.Should().Be(expected.B);
}

[Fact]
public void CtorParamDefaultValue()
{
var bin = MemoryPackSerializer.Serialize(new DefaultValuePlaceholder { X = 1 });
var expected = new CtorParamDefaultValue(1);
var deserializedValue = MemoryPackSerializer.Deserialize<CtorParamDefaultValue>(bin)!;
deserializedValue.Y.Should().Be(expected.Y);
deserializedValue.Z.Should().Be(expected.Z);
deserializedValue.S.Should().Be(expected.S);
deserializedValue.B.Should().Be(expected.B);
}
}
47 changes: 47 additions & 0 deletions tests/MemoryPack.Tests/Models/DefaultValues.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace MemoryPack.Tests.Models;

[MemoryPackable]
partial class DefaultValuePlaceholder
{
public int X { get; set; }
}

[MemoryPackable]
partial class FieldDefaultValue
{
public int X;
public int Y = 12345;
public float Z = 678.9f;
public string S = "aaaaaaaaa";
public bool B = true;
}

[MemoryPackable]
partial class PropertyDefaultValue
{
public int X { get; set; }
public int Y { get; set; } = 12345;
public float Z { get; set; } = 678.9f;
public string S { get; set; } = "aaaaaaaaa";
public bool B { get; set; } = true;
}

[MemoryPackable]
partial class CtorParamDefaultValue
{
public int X;
public int Y;
public float Z;
public string S;
public bool B;

[MemoryPackConstructor]
public CtorParamDefaultValue(int x, int y = 12345, float z = 678.9f, string s = "aaaaaa", bool b = true)
{
X = x;
Y = y;
Z = z;
S = s;
B = b;
}
}

0 comments on commit 285ac2a

Please sign in to comment.