Skip to content

Commit

Permalink
Merge pull request #1802 from MessagePack-CSharp/fix1745
Browse files Browse the repository at this point in the history
Source generated formatters that support private members
  • Loading branch information
AArnott committed May 7, 2024
2 parents dff76a1 + c76d56c commit 3d80df1
Show file tree
Hide file tree
Showing 212 changed files with 2,305 additions and 1,109 deletions.
1 change: 1 addition & 0 deletions doc/analyzers/MsgPack006.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# MsgPack006 Type must be of `IMessagePackFormatter`
1 change: 1 addition & 0 deletions doc/analyzers/MsgPack007.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# MsgPack007 Deserializing constructors
2 changes: 2 additions & 0 deletions doc/analyzers/MsgPack008.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# MsgPack008 AOT limitations

1 change: 1 addition & 0 deletions doc/analyzers/MsgPack009.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# MsgPack009 Colliding Formatters
1 change: 1 addition & 0 deletions doc/analyzers/MsgPack010.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# MsgPack010 Inaccessible Formatter
1 change: 1 addition & 0 deletions doc/analyzers/MsgPack011.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# MsgPack011 Partial type required
1 change: 1 addition & 0 deletions doc/analyzers/MsgPack012.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# MsgPack012 Inaccessible data type
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ MsgPack006 | Usage | Error | MsgPack00xMessagePackAnalyzer
MsgPack007 | Usage | Error | MsgPack00xMessagePackAnalyzer
MsgPack008 | Usage | Error | MsgPack00xMessagePackAnalyzer
MsgPack009 | Usage | Error | MsgPack00xMessagePackAnalyzer
MsgPack010 | Usage | Warning | MsgPack00xMessagePackAnalyzer
MsgPack010 | Usage | Warning | MsgPack00xMessagePackAnalyzer
MsgPack011 | Usage | Error | MsgPack00xMessagePackAnalyzer
MsgPack012 | Usage | Error | MsgPack00xMessagePackAnalyzer
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer
public const string AOTLimitationsId = "MsgPack008";
public const string CollidingFormattersId = "MsgPack009";
public const string InaccessibleFormatterId = "MsgPack010";
public const string PartialTypeRequiredId = "MsgPack011";
public const string InaccessibleDataTypeId = "MsgPack012";

internal const string Category = "Usage";

Expand All @@ -27,6 +29,9 @@ public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer
internal const string IgnoreShortName = "IgnoreMemberAttribute";
internal const string IgnoreDataMemberShortName = "IgnoreDataMemberAttribute";

private const string InvalidMessagePackObjectTitle = "MessagePackObject validation";
private const DiagnosticSeverity InvalidMessagePackObjectSeverity = DiagnosticSeverity.Error;

internal static readonly DiagnosticDescriptor TypeMustBeMessagePackObject = new DiagnosticDescriptor(
id: UseMessagePackObjectAttributeId,
title: "Use MessagePackObjectAttribute",
Expand Down Expand Up @@ -69,51 +74,63 @@ public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer

internal static readonly DiagnosticDescriptor InvalidMessagePackObject = new DiagnosticDescriptor(
id: InvalidMessagePackObjectId,
title: "MessagePackObject validation",
title: InvalidMessagePackObjectTitle,
category: Category,
messageFormat: "Invalid MessagePackObject definition: {0}", // details
description: "Invalid MessagePackObject definition.",
defaultSeverity: DiagnosticSeverity.Error,
defaultSeverity: InvalidMessagePackObjectSeverity,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(InvalidMessagePackObjectId));

internal static readonly DiagnosticDescriptor BothStringAndIntKeyAreNull = new DiagnosticDescriptor(
id: InvalidMessagePackObjectId,
title: "Attribute public members of MessagePack objects",
title: InvalidMessagePackObjectTitle,
category: Category,
messageFormat: "Both int and string keys are null: {0}.{1}", // type.Name + "." + item.Name
description: "An int or string key must be supplied to the KeyAttribute.",
defaultSeverity: DiagnosticSeverity.Error,
defaultSeverity: InvalidMessagePackObjectSeverity,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(InvalidMessagePackObjectId));

internal static readonly DiagnosticDescriptor DoNotMixStringAndIntKeys = new DiagnosticDescriptor(
id: InvalidMessagePackObjectId,
title: "Attribute public members of MessagePack objects",
title: InvalidMessagePackObjectTitle,
category: Category,
messageFormat: "All KeyAttribute arguments must be of the same type (either string or int)",
description: "Use string or int keys consistently.",
defaultSeverity: DiagnosticSeverity.Error,
defaultSeverity: InvalidMessagePackObjectSeverity,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(InvalidMessagePackObjectId));

internal static readonly DiagnosticDescriptor KeysMustBeUnique = new DiagnosticDescriptor(
id: InvalidMessagePackObjectId,
title: "Attribute public members of MessagePack objects",
title: InvalidMessagePackObjectTitle,
category: Category,
messageFormat: "All KeyAttribute arguments must be unique",
description: "Each key must be unique.",
defaultSeverity: DiagnosticSeverity.Error,
defaultSeverity: InvalidMessagePackObjectSeverity,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(InvalidMessagePackObjectId));

internal static readonly DiagnosticDescriptor UnionAttributeRequired = new DiagnosticDescriptor(
id: InvalidMessagePackObjectId,
title: "Attribute public members of MessagePack objects",
title: InvalidMessagePackObjectTitle,
category: Category,
messageFormat: "This type must carry a UnionAttribute",
description: "A UnionAttribute is required on interfaces and abstract base classes used as serialized types.",
defaultSeverity: DiagnosticSeverity.Error,
defaultSeverity: InvalidMessagePackObjectSeverity,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(InvalidMessagePackObjectId));

// This is important because [Key] on a private member still will not be serialized, which is very confusing until
// one realizes the type is serializing in map mode.
internal static readonly DiagnosticDescriptor KeyAnnotatedMemberInMapMode = new DiagnosticDescriptor(
id: InvalidMessagePackObjectId,
title: InvalidMessagePackObjectTitle,
category: Category,
messageFormat: "Types in map mode should not annotate members with KeyAttribute",
description: "When in map mode (by compilation setting or with [MessagePackObject(true)]), internal and public members are automatically included in serialization and should not be annotated with KeyAttribute.",
defaultSeverity: InvalidMessagePackObjectSeverity,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(InvalidMessagePackObjectId));

Expand Down Expand Up @@ -207,13 +224,48 @@ public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(InaccessibleFormatterId));

internal static readonly DiagnosticDescriptor PartialTypeRequired = new(
id: PartialTypeRequiredId,
title: "Partial type required",
category: Category,
messageFormat: "Types with private, serializable members must be declared as partial",
description: "When a data type has serializable members that may only be accessible to the class itself (e.g. private or protected members), the type must be declared as partial to allow source generation of the formatter as a nested type.",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(PartialTypeRequiredId));

internal static readonly DiagnosticDescriptor InaccessibleDataType = new(
id: InaccessibleDataTypeId,
title: "Internally accessible data type required",
category: Category,
messageFormat: "This MessagePack formattable type must have at least internal visibility",
description: "MessagePack serializable objects must be at least internally accessible so a source-generated formatter can access it.",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(InaccessibleDataTypeId));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
TypeMustBeMessagePackObject,
MessageFormatterMustBeMessagePackFormatter,
PublicMemberNeedsKey,
BaseTypeContainsUnattributedPublicMembers,
InvalidMessagePackObject,
MessageFormatterMustBeMessagePackFormatter,
BothStringAndIntKeyAreNull,
DoNotMixStringAndIntKeys,
KeysMustBeUnique,
UnionAttributeRequired,
KeyAnnotatedMemberInMapMode,
NoDeserializingConstructor,
DeserializingConstructorParameterTypeMismatch,
DeserializingConstructorParameterIndexMissing,
DeserializingConstructorParameterNameMissing,
DeserializingConstructorParameterNameDuplicate,
AotUnionAttributeRequiresTypeArg,
AotArrayRankTooHigh,
CollidingFormatters,
InaccessibleFormatter);
InaccessibleFormatter,
PartialTypeRequired,
InaccessibleDataType);

public override void Initialize(AnalysisContext context)
{
Expand Down Expand Up @@ -258,7 +310,7 @@ private void AnalyzeSymbol(SymbolAnalysisContext context, ReferenceSymbols typeR
{
case TypeKind.Interface when declaredSymbol.GetAttributes().Any(x2 => SymbolEqualityComparer.Default.Equals(x2.AttributeClass, typeReferences.UnionAttribute)):
case TypeKind.Class or TypeKind.Struct when declaredSymbol.GetAttributes().Any(x2 => SymbolEqualityComparer.Default.Equals(x2.AttributeClass, typeReferences.MessagePackObjectAttribute)):
TypeCollector.Collect(context.Compilation, options, typeReferences, context.ReportDiagnostic, declaredSymbol);
TypeCollector.Collect(context.Compilation, options, typeReferences, context.ReportDiagnostic, declaredSymbol, context.CancellationToken);
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ namespace MessagePack.SourceGenerator.CodeAnalysis;

public record CustomFormatterRegisterInfo : ResolverRegisterInfo
{
public override string GetFormatterNameForResolver(GenericParameterStyle style) => this.Formatter.GetQualifiedName(Qualifiers.GlobalNamespace, style);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ public sealed record EnumSerializationInfo : ResolverRegisterInfo
_ => this.UnderlyingTypeName,
};

public static EnumSerializationInfo Create(INamedTypeSymbol dataType, ISymbol enumUnderlyingType)
public static EnumSerializationInfo Create(INamedTypeSymbol dataType, ISymbol enumUnderlyingType, ResolverOptions resolverOptions)
{
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(dataType);
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(dataType, resolverOptions);
return new EnumSerializationInfo
{
DataType = basicInfo.DataType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public sealed record GenericSerializationInfo : ResolverRegisterInfo
{
public override bool IsUnboundGenericType => false;

public static new GenericSerializationInfo Create(ITypeSymbol dataType)
public static new GenericSerializationInfo Create(ITypeSymbol dataType, ResolverOptions resolverOptions, FormatterPosition formatterLocation = FormatterPosition.UnderResolver)
{
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(dataType);
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(dataType, resolverOptions, formatterLocation);
ImmutableArray<string> typeArguments = CodeAnalysisUtilities.GetTypeArguments(dataType);
return new GenericSerializationInfo
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public record ObjectSerializationInfo : ResolverRegisterInfo
{
public required bool IsClass { get; init; }

public bool IncludesPrivateMembers { get; init; }

public required GenericTypeParameterInfo[] GenericTypeParameters { get; init; }

public required MemberSerializationInfo[] ConstructorParameters { get; init; }
Expand Down Expand Up @@ -52,20 +54,26 @@ public int MaxKey
public static ObjectSerializationInfo Create(
INamedTypeSymbol dataType,
bool isClass,
bool includesPrivateMembers,
GenericTypeParameterInfo[] genericTypeParameters,
MemberSerializationInfo[] constructorParameters,
bool isIntKey,
MemberSerializationInfo[] members,
bool hasIMessagePackSerializationCallbackReceiver,
bool needsCastOnAfter,
bool needsCastOnBefore)
bool needsCastOnBefore,
ResolverOptions resolverOptions)
{
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(dataType);
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(
dataType,
resolverOptions,
includesPrivateMembers ? FormatterPosition.UnderDataType : FormatterPosition.UnderResolver);
return new ObjectSerializationInfo
{
DataType = basicInfo.DataType,
Formatter = basicInfo.Formatter,
IsClass = isClass,
IncludesPrivateMembers = includesPrivateMembers,
GenericTypeParameters = genericTypeParameters,
ConstructorParameters = constructorParameters,
IsIntKey = isIntKey,
Expand Down

0 comments on commit 3d80df1

Please sign in to comment.