Skip to content

Commit

Permalink
Merge pull request #1259 from icanhasjonas/master
Browse files Browse the repository at this point in the history
Allow object that has either `[MessagePackObject]` or a valid `[MessagePackFormatter()]`
  • Loading branch information
AArnott committed Jun 7, 2021
2 parents 7ca890e + 070f3a9 commit af6d26a
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 6 deletions.
14 changes: 13 additions & 1 deletion src/MessagePackAnalyzer/MessagePackAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class MessagePackAnalyzer : DiagnosticAnalyzer
public const string UseMessagePackObjectAttributeId = "MsgPack003";
public const string AttributeMessagePackObjectMembersId = "MsgPack004";
public const string InvalidMessagePackObjectId = "MsgPack005";
public const string MessagePackFormatterMustBeMessagePackFormatterId = "MsgPack006";

internal const string Category = "Usage";

Expand All @@ -35,6 +36,16 @@ public class MessagePackAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(UseMessagePackObjectAttributeId));

internal static readonly DiagnosticDescriptor MessageFormatterMustBeMessagePackFormatter = new DiagnosticDescriptor(
id: MessagePackFormatterMustBeMessagePackFormatterId,
title: "Must be IMessageFormatter",
category: Category,
messageFormat: "Type must be of IMessagePackFormatter. {0}.", // type.Name
description: "Type must be of IMessagePackFormatter.",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(UseMessagePackObjectAttributeId));

internal static readonly DiagnosticDescriptor PublicMemberNeedsKey = new DiagnosticDescriptor(
id: AttributeMessagePackObjectMembersId,
title: "Attribute public members of MessagePack objects",
Expand All @@ -58,7 +69,8 @@ public class MessagePackAnalyzer : DiagnosticAnalyzer
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
TypeMustBeMessagePackObject,
PublicMemberNeedsKey,
InvalidMessagePackObject);
InvalidMessagePackObject,
MessageFormatterMustBeMessagePackFormatter);

public override void Initialize(AnalysisContext context)
{
Expand Down
22 changes: 22 additions & 0 deletions src/MessagePackAnalyzer/ReferenceSymbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ public class ReferenceSymbols
INamedTypeSymbol unionAttribute,
INamedTypeSymbol keyAttribute,
INamedTypeSymbol ignoreAttribute,
INamedTypeSymbol formatterAttribute,
INamedTypeSymbol messagePackFormatter,
INamedTypeSymbol ignoreDataMemberAttribute)
{
this.MessagePackObjectAttribute = messagePackObjectAttribute;
this.UnionAttribute = unionAttribute;
this.KeyAttribute = keyAttribute;
this.IgnoreAttribute = ignoreAttribute;
this.FormatterAttribute = formatterAttribute;
this.MessagePackFormatter = messagePackFormatter;
this.IgnoreDataMemberAttribute = ignoreDataMemberAttribute;
}

Expand All @@ -30,6 +34,10 @@ public class ReferenceSymbols

internal INamedTypeSymbol IgnoreAttribute { get; }

internal INamedTypeSymbol FormatterAttribute { get; }

internal INamedTypeSymbol MessagePackFormatter { get; }

internal INamedTypeSymbol IgnoreDataMemberAttribute { get; }

public static bool TryCreate(Compilation compilation, [NotNullWhen(true)] out ReferenceSymbols? instance)
Expand Down Expand Up @@ -60,6 +68,18 @@ public static bool TryCreate(Compilation compilation, [NotNullWhen(true)] out Re
return false;
}

var formatterAttribute = compilation.GetTypeByMetadataName("MessagePack.MessagePackFormatterAttribute");
if (formatterAttribute is null)
{
return false;
}

var messageFormatter = compilation.GetTypeByMetadataName("MessagePack.Formatters.IMessagePackFormatter");
if (messageFormatter is null)
{
return false;
}

var ignoreDataMemberAttribute = compilation.GetTypeByMetadataName("System.Runtime.Serialization.IgnoreDataMemberAttribute");
if (ignoreDataMemberAttribute is null)
{
Expand All @@ -71,6 +91,8 @@ public static bool TryCreate(Compilation compilation, [NotNullWhen(true)] out Re
unionAttribute,
keyAttribute,
ignoreAttribute,
formatterAttribute,
messageFormatter,
ignoreDataMemberAttribute);
return true;
}
Expand Down
16 changes: 16 additions & 0 deletions src/MessagePackAnalyzer/TypeCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,22 @@ private void CollectObject(INamedTypeSymbol type, ISymbol? callerSymbol)
{
var isClass = !type.IsValueType;

AttributeData formatterAttr = type.GetAttributes().FirstOrDefault(x => Equals(x.AttributeClass, this.typeReferences.FormatterAttribute));
if (formatterAttr != null)
{
// Validate that the typed formatter is actually of `IMessagePackFormatter`
var formatterType = (ITypeSymbol)formatterAttr.ConstructorArguments[0].Value;
var isMessagePackFormatter = formatterType.AllInterfaces.Any(x => x.Equals(this.typeReferences.MessagePackFormatter));
if (!isMessagePackFormatter)
{
var location = formatterAttr.ApplicationSyntaxReference.SyntaxTree.GetLocation(formatterAttr.ApplicationSyntaxReference.Span);
var typeInfo = ImmutableDictionary.Create<string, string>().Add("type", formatterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
this.ReportContext.Add(Diagnostic.Create(MessagePackAnalyzer.MessageFormatterMustBeMessagePackFormatter, location, typeInfo));
}

return;
}

AttributeData contractAttr = type.GetAttributes().FirstOrDefault(x => Equals(x.AttributeClass, this.typeReferences.MessagePackObjectAttribute));
if (contractAttr == null)
{
Expand Down
63 changes: 58 additions & 5 deletions tests/MessagePackAnalyzer.Tests/MessagePackAnalyzerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
using VerifyCS = CSharpCodeFixVerifier<MessagePackAnalyzer.MessagePackAnalyzer, MessagePackAnalyzer.MessagePackCodeFixProvider>;
using VerifyCS =
CSharpCodeFixVerifier<MessagePackAnalyzer.MessagePackAnalyzer, MessagePackAnalyzer.MessagePackCodeFixProvider>;

public class MessagePackAnalyzerTests
{
Expand All @@ -23,6 +26,54 @@ public class Foo
await VerifyCS.VerifyAnalyzerWithoutMessagePackReferenceAsync(input);
}

[Fact]
public async Task MessageFormatterAttribute()
{
string input = Preamble + @"using MessagePack.Formatters;
public class FooFormatter : IMessagePackFormatter<Foo> {
public void Serialize(ref MessagePackWriter writer, Foo value, MessagePackSerializerOptions options) {}
public Foo Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) => default;
}
[MessagePackFormatter(typeof(FooFormatter))]
public struct Foo
{
}
[MessagePackObject]
public class SomeClass {
[Key(0)]
public Foo SomeFoo { get; set; }
}
";

await VerifyCS.VerifyAnalyzerAsync(input);
}

[Fact]
public async Task InvalidMessageFormatterType()
{
string input = Preamble + @"using MessagePack.Formatters;
public class InvalidMessageFormatter { }
[{|MsgPack006:MessagePackFormatter(typeof(InvalidMessageFormatter))|}]
public struct Foo
{
}
[MessagePackObject]
public class SomeClass {
[Key(0)]
public Foo SomeFoo { get; set; }
}
";

await VerifyCS.VerifyAnalyzerAsync(input);
}

[Fact]
public async Task NullStringKey()
{
Expand Down Expand Up @@ -106,12 +157,13 @@ public async Task CodeFixAppliesAcrossFiles()
{
var inputs = new string[]
{
@"
@"
public class Foo
{
public int {|MsgPack004:Member1|} { get; set; }
}
", @"using MessagePack;
",
@"using MessagePack;
[MessagePackObject]
public class Bar : Foo
Expand All @@ -122,13 +174,14 @@ public class Bar : Foo
};
var outputs = new string[]
{
@"
@"
public class Foo
{
[MessagePack.Key(1)]
public int Member1 { get; set; }
}
", @"using MessagePack;
",
@"using MessagePack;
[MessagePackObject]
public class Bar : Foo
Expand Down

0 comments on commit af6d26a

Please sign in to comment.