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
56 changes: 56 additions & 0 deletions doc/analyzers/MsgPack018.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# MsgPack018 Unique names required in force map mode

`[MessagePackObject]`-attributed types may omit attributing each member with a `[Key]` attribute using forced map mode.
In that mode, all serialized members *must* have unique names or a key collision would result in the serialized object.

## Examples of patterns that are flagged by this analyzer

```cs
[MessagePackObject]
public class A
{
public string Prop1 { get; set; }
}

[MessagePackObject]
public class B : A
{
public new string Prop1 { get; set; } // Diagnostic reported here due to redefinition of Prop1
}
```

## Typical fix

Rename one of the colliding properties:

```cs
[MessagePackObject]
public class A
{
public string Prop1 { get; set; }
}

[MessagePackObject]
public class B : A
{
public string Prop2 { get; set; }
}
```

Or add a `[Key]` attribute that assigns a unique serialized key to that member:


```cs
[MessagePackObject]
public class A
{
public string Prop1 { get; set; }
}

[MessagePackObject]
public class B : A
{
[Key("B_Prop1")]
public new string Prop1 { get; set; }
}
```
1 change: 1 addition & 0 deletions doc/analyzers/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ ID | Title
[MsgPack015](MsgPack015.md) | MessagePackObjectAttribute.AllowPrivate should be set
[MsgPack016](MsgPack016.md) | KeyAttribute-derived attributes are not supported by AOT formatters
[MsgPack017](MsgPack017.md) | Property with init accessor and initializer
[MsgPack018](MsgPack018.md) | Unique names required in force map mode

[1]: https://nuget.org/packages/MessagePackAnalyzer
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ MsgPack014 | Usage | Warning | Formatters of reference types should implement `I
MsgPack015 | Usage | Warning | MessagePackObjectAttribute.AllowPrivate should be set
MsgPack016 | Usage | Error | KeyAttribute-derived attributes are not supported by AOT formatters
MsgPack017 | Usage | Warning | Property with init accessor and initializer
MsgPack018 | Usage | Error | Unique names required in force map mode
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer
public const string MessagePackObjectAllowPrivateId = "MsgPack015";
public const string AOTDerivedKeyId = "MsgPack016";
public const string AOTInitPropertyId = "MsgPack017";
public const string CollidingMemberNamesInForceMapModeId = "MsgPack018";

internal const string Category = "Usage";

Expand Down Expand Up @@ -301,6 +302,15 @@ public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(MessagePackObjectAllowPrivateId));

public static readonly DiagnosticDescriptor CollidingMemberNamesInForceMapMode = new(
id: CollidingMemberNamesInForceMapModeId,
title: "Unique names required in force map mode",
category: Category,
messageFormat: "All serialized member names must be unique in force map mode, but this member redeclares a member with the same name as one found on a base type",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(CollidingMemberNamesInForceMapModeId));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
TypeMustBeMessagePackObject,
MessageFormatterMustBeMessagePackFormatter,
Expand All @@ -327,7 +337,8 @@ public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer
PartialTypeRequired,
InaccessibleDataType,
NullableReferenceTypeFormatter,
MessagePackObjectAllowPrivateRequired);
MessagePackObjectAllowPrivateRequired,
CollidingMemberNamesInForceMapMode);

public override void Initialize(AnalysisContext context)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,32 @@ public record MemberSerializationInfo(
{
private static readonly IReadOnlyCollection<string> PrimitiveTypes = new HashSet<string>(AnalyzerUtilities.PrimitiveTypes);

public string LocalVariableName => $"__{this.Name}__";
public string LocalVariableName => $"__{this.UniqueIdentifier}__";

public string UniqueIdentifier => this.DeclaringType is null ? this.Name : $"{this.DeclaringType.Name}_{this.Name}";

/// <summary>
/// Gets the declaring type of this member, if a derived type declares a new member
/// with the same identifier, thus requiring a source generator to cast
/// the target to the member's declaring type in order to access it.
/// </summary>
public required QualifiedNamedTypeName? DeclaringType { get; init; }

public string GetSerializeMethodString()
{
string memberRead = this.GetMemberAccess("value");

if (CustomFormatter is not null)
{
return $"this.__{this.Name}CustomFormatter__.Serialize(ref writer, value.{this.Name}, options)";
return $"this.__{this.Name}CustomFormatter__.Serialize(ref writer, {memberRead}, options)";
}
else if (PrimitiveTypes.Contains(this.Type))
{
return "writer.Write(value." + this.Name + ")";
}
else
{
return $"MsgPack::FormatterResolverExtensions.GetFormatterWithVerify<{this.Type}>(formatterResolver).Serialize(ref writer, value.{this.Name}, options)";
return $"MsgPack::FormatterResolverExtensions.GetFormatterWithVerify<{this.Type}>(formatterResolver).Serialize(ref writer, {memberRead}, options)";
}
}

Expand All @@ -58,4 +69,6 @@ public string GetDeserializeMethodString()
return $"MsgPack::FormatterResolverExtensions.GetFormatterWithVerify<{this.Type}>(formatterResolver).Deserialize(ref reader, options)";
}
}

public string GetMemberAccess(string targetObject) => this.DeclaringType is null ? $"{targetObject}.{this.Name}" : $"(({this.DeclaringType.GetQualifiedName()}){targetObject}).{this.Name}";
}
Loading