Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer
title: "Deserializing constructors",
category: Category,
messageFormat: "Deserializing constructor parameter count mismatch",
description: "Constructor parameter count must meet or exceed the number of serialized members or the highest key index.",
description: "The deserializing constructor parameter count must meet or exceed the number of serialized members.",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(DeserializingConstructorId));
Expand Down
19 changes: 17 additions & 2 deletions src/MessagePack.SourceGenerator/CodeAnalysis/TypeCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma warning disable SA1649 // File name should match first type name

using System.Collections.Immutable;
using System.ComponentModel;
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -682,6 +683,7 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
{
var isClass = !formattedType.IsValueType;
bool nestedFormatterRequired = false;
List<IPropertySymbol> nestedFormatterRequiredIfPropertyIsNotSetByDeserializingCtor = new();

AttributeData? contractAttr = formattedType.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackObjectAttribute));

Expand Down Expand Up @@ -875,7 +877,10 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
nonPublicMembersAreSerialized |= (item.DeclaredAccessibility & Accessibility.Public) != Accessibility.Public;

nestedFormatterRequired |= item.GetMethod is not null && IsPartialTypeRequired(item.GetMethod.DeclaredAccessibility);
nestedFormatterRequired |= item.SetMethod is not null && IsPartialTypeRequired(item.SetMethod.DeclaredAccessibility);
if (item.SetMethod is not null && IsPartialTypeRequired(item.SetMethod.DeclaredAccessibility))
{
nestedFormatterRequiredIfPropertyIsNotSetByDeserializingCtor.Add(item);
}

var intKey = key is { Value: int intKeyValue } ? intKeyValue : default(int?);
var stringKey = key is { Value: string stringKeyValue } ? stringKeyValue : default;
Expand Down Expand Up @@ -1062,6 +1067,9 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
nestedFormatterRequired |= IsPartialTypeRequired(ctor.DeclaredAccessibility);

var constructorLookupDictionary = stringMembers.ToLookup(x => x.Value.Info.Name, x => x, StringComparer.OrdinalIgnoreCase);
IReadOnlyDictionary<int, (MemberSerializationInfo Info, ITypeSymbol TypeSymbol)> ctorParamIndexIntMembersDictionary = intMembers
.OrderBy(x => x.Key).Select((x, i) => (Key: x.Value, Index: i))
.ToDictionary(x => x.Index, x => x.Key);
do
{
constructorParameters.Clear();
Expand All @@ -1070,7 +1078,7 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
{
if (isIntKey)
{
if (intMembers.TryGetValue(ctorParamIndex, out (MemberSerializationInfo Info, ITypeSymbol TypeSymbol) member))
if (ctorParamIndexIntMembersDictionary.TryGetValue(ctorParamIndex, out (MemberSerializationInfo Info, ITypeSymbol TypeSymbol) member))
{
if (this.compilation.ClassifyConversion(member.TypeSymbol, item.Type) is { IsImplicit: true } && member.Info.IsReadable)
{
Expand Down Expand Up @@ -1204,6 +1212,13 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
return null;
}

// If any property had a private setter and does not appear in the deserializing constructor signature,
// we'll need a nested formatter.
foreach (IPropertySymbol property in nestedFormatterRequiredIfPropertyIsNotSetByDeserializingCtor)
{
nestedFormatterRequired |= !constructorParameters.Any(m => m.Name == property.Name);
}

if (nestedFormatterRequired && nonPublicMembersAreSerialized)
{
// If the data type or any nesting types are not declared with partial, we cannot emit the formatter as a nested type within the data type
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

[MessagePackObject]
public class DeserializingConstructorStartsWithIdx1 : IEquatable<DeserializingConstructorStartsWithIdx1>
{
[Key(1)] // 1 instead of 0. Works on v2. Test case from https://github.com/MessagePack-CSharp/MessagePack-CSharp/issues/1993
public string Name { get; }

[SerializationConstructor]
public DeserializingConstructorStartsWithIdx1(string name) => Name = name;

public bool Equals(DeserializingConstructorStartsWithIdx1? other) => other is not null && this.Name == other.Name;
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ public void ClassWithRequiredMembers()
this.AssertRoundtrip(new HasRequiredMembers { A = 1, B = 4, C = 5, D = 6 });
}

[Fact]
public void DeserializingConstructorStartsWithIdx1()
{
this.AssertRoundtrip(new DeserializingConstructorStartsWithIdx1("foo"));
}

#if !FORCE_MAP_MODE // forced map mode simply doesn't support private fields at all as it only notices internal and public members.
[Fact]
public void PrivateFieldIsSerialized()
Expand Down
20 changes: 20 additions & 0 deletions tests/MessagePack.SourceGenerator.Tests/GenerationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -672,4 +672,24 @@ class A {
ExpectedDiagnostics = { DiagnosticResult.CompilerError("MsgPack007").WithLocation(0) },
}.RunDefaultAsync(this.testOutputHelper);
}

[Fact]
public async Task NoPrivateAccessNeeded()
{
// Test case came from https://github.com/MessagePack-CSharp/MessagePack-CSharp/issues/1993
string testSource = /* lang=c#-test */ """
using MessagePack;

[MessagePackObject]
public class Test3
{
[Key(0)]
public string Name { get; private set; }

[SerializationConstructor]
public Test3(string name) => Name = name;
}
""";
await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// <auto-generated />

#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649

#pragma warning disable CS8669 // We may leak nullable annotations into generated code.

using MsgPack = global::MessagePack;

namespace MessagePack {
partial class GeneratedMessagePackResolver {

internal sealed class Test3Formatter : MsgPack::Formatters.IMessagePackFormatter<global::Test3>
{

public void Serialize(ref MsgPack::MessagePackWriter writer, global::Test3 value, MsgPack::MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}

MsgPack::IFormatterResolver formatterResolver = options.Resolver;
writer.WriteArrayHeader(1);
MsgPack::FormatterResolverExtensions.GetFormatterWithVerify<string>(formatterResolver).Serialize(ref writer, value.Name, options);
}

public global::Test3 Deserialize(ref MsgPack::MessagePackReader reader, MsgPack::MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}

options.Security.DepthStep(ref reader);
MsgPack::IFormatterResolver formatterResolver = options.Resolver;
var length = reader.ReadArrayHeader();
var __Name__ = default(string);

for (int i = 0; i < length; i++)
{
switch (i)
{
case 0:
__Name__ = MsgPack::FormatterResolverExtensions.GetFormatterWithVerify<string>(formatterResolver).Deserialize(ref reader, options);
break;
default:
reader.Skip();
break;
}
}

var ____result = new global::Test3(__Name__);
reader.Depth--;
return ____result;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// <auto-generated />

#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649

using MsgPack = global::MessagePack;

[assembly: MsgPack::Internal.GeneratedAssemblyMessagePackResolverAttribute(typeof(MessagePack.GeneratedMessagePackResolver), 3, 0)]

namespace MessagePack {

/// <summary>A MessagePack resolver that uses generated formatters for types in this assembly.</summary>
partial class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
{
/// <summary>An instance of this resolver that only returns formatters specifically generated for types in this assembly.</summary>
public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();

private GeneratedMessagePackResolver()
{
}

public MsgPack::Formatters.IMessagePackFormatter<T> GetFormatter<T>()
{
return FormatterCache<T>.Formatter;
}

private static class FormatterCache<T>
{
internal static readonly MsgPack::Formatters.IMessagePackFormatter<T> Formatter;

static FormatterCache()
{
var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
if (f != null)
{
Formatter = (MsgPack::Formatters.IMessagePackFormatter<T>)f;
}
}
}

private static class GeneratedMessagePackResolverGetFormatterHelper
{
private static readonly global::System.Collections.Generic.Dictionary<global::System.Type, int> closedTypeLookup = new global::System.Collections.Generic.Dictionary<global::System.Type, int>(1)
{
{ typeof(global::Test3), 0 },
};

internal static object GetFormatter(global::System.Type t)
{
if (closedTypeLookup.TryGetValue(t, out int closedKey))
{
switch (closedKey)
{
case 0: return new global::MessagePack.GeneratedMessagePackResolver.Test3Formatter();
default: return null; // unreachable
};
}

return null;
}
}
}

}