diff --git a/Directory.Packages.props b/Directory.Packages.props
index bc598ec73..e5036e889 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -27,17 +27,18 @@
-
+
-
+
-
+
+
diff --git a/MessagePack.sln b/MessagePack.sln
index dd79fbec0..b22e29301 100644
--- a/MessagePack.sln
+++ b/MessagePack.sln
@@ -24,7 +24,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{344DC89D-8
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
LICENSE = LICENSE
- doc\MessagePackAnalyzer.json = doc\MessagePackAnalyzer.json
README.md = README.md
EndProjectSection
EndProject
@@ -42,8 +41,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.ReactivePropert
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.ImmutableCollection", "src\MessagePack.ImmutableCollection\MessagePack.ImmutableCollection.csproj", "{E066F547-7261-4561-AEFC-E64DBFD874F8}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Analyzers", "src\MessagePack.Analyzers\MessagePack.Analyzers.csproj", "{2F9A6E0C-DE95-4460-96B7-EB72BBEAEE9E}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfNetFramework", "sandbox\PerfNetFramework\PerfNetFramework.csproj", "{014A3DCE-50A6-4774-A4C1-C66EEAB67133}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.AspNetCoreMvcFormatter", "src\MessagePack.AspNetCoreMvcFormatter\MessagePack.AspNetCoreMvcFormatter.csproj", "{17831017-C29C-4A48-B159-849BCE5079FB}"
@@ -84,8 +81,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Internal.Tests"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.SourceGenerator", "src\MessagePack.SourceGenerator\MessagePack.SourceGenerator.csproj", "{32C91908-5CAD-4C95-B240-ACBBACAC9476}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Analyzers.Tests", "tests\MessagePack.Analyzers.Tests\MessagePack.Analyzers.Tests.csproj", "{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.SourceGenerator.Tests", "tests\MessagePack.SourceGenerator.Tests\MessagePack.SourceGenerator.Tests.csproj", "{6AC51E68-4681-463A-B4B6-BD53517244B2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExperimentalBenchmark", "benchmark\ExperimentalBenchmark\ExperimentalBenchmark.csproj", "{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637}"
@@ -106,6 +101,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.SourceGenerator
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Analyzers.CodeFixes", "src\MessagePack.Analyzers.CodeFixes\MessagePack.Analyzers.CodeFixes.csproj", "{7A6CB600-2393-468F-9952-84EC624D57BD}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagePack.Analyzers.CodeFixes.Unity", "src\MessagePack.Analyzers.CodeFixes.Unity\MessagePack.Analyzers.CodeFixes.Unity.csproj", "{4E467A70-6188-4409-B29B-E1E7DD8CDA3C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagePack.Analyzers", "src\MessagePack.Analyzers\MessagePack.Analyzers.csproj", "{EB77463C-9D06-4AAE-84F0-470988D30DA0}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CollectionsMarshalBenchmark", "benchmark\CollectionsMarshalBenchmark\CollectionsMarshalBenchmark.csproj", "{9A31C44C-9C51-4D41-B8E5-2864245F877E}"
EndProject
Global
@@ -146,10 +145,6 @@ Global
{E066F547-7261-4561-AEFC-E64DBFD874F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E066F547-7261-4561-AEFC-E64DBFD874F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E066F547-7261-4561-AEFC-E64DBFD874F8}.Release|Any CPU.Build.0 = Release|Any CPU
- {2F9A6E0C-DE95-4460-96B7-EB72BBEAEE9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2F9A6E0C-DE95-4460-96B7-EB72BBEAEE9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2F9A6E0C-DE95-4460-96B7-EB72BBEAEE9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2F9A6E0C-DE95-4460-96B7-EB72BBEAEE9E}.Release|Any CPU.Build.0 = Release|Any CPU
{014A3DCE-50A6-4774-A4C1-C66EEAB67133}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{014A3DCE-50A6-4774-A4C1-C66EEAB67133}.Debug|Any CPU.Build.0 = Debug|Any CPU
{014A3DCE-50A6-4774-A4C1-C66EEAB67133}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -190,10 +185,6 @@ Global
{32C91908-5CAD-4C95-B240-ACBBACAC9476}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32C91908-5CAD-4C95-B240-ACBBACAC9476}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32C91908-5CAD-4C95-B240-ACBBACAC9476}.Release|Any CPU.Build.0 = Release|Any CPU
- {7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}.Release|Any CPU.Build.0 = Release|Any CPU
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -234,6 +225,14 @@ Global
{7A6CB600-2393-468F-9952-84EC624D57BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A6CB600-2393-468F-9952-84EC624D57BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A6CB600-2393-468F-9952-84EC624D57BD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4E467A70-6188-4409-B29B-E1E7DD8CDA3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4E467A70-6188-4409-B29B-E1E7DD8CDA3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4E467A70-6188-4409-B29B-E1E7DD8CDA3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4E467A70-6188-4409-B29B-E1E7DD8CDA3C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EB77463C-9D06-4AAE-84F0-470988D30DA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EB77463C-9D06-4AAE-84F0-470988D30DA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EB77463C-9D06-4AAE-84F0-470988D30DA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EB77463C-9D06-4AAE-84F0-470988D30DA0}.Release|Any CPU.Build.0 = Release|Any CPU
{9A31C44C-9C51-4D41-B8E5-2864245F877E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A31C44C-9C51-4D41-B8E5-2864245F877E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A31C44C-9C51-4D41-B8E5-2864245F877E}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -251,7 +250,6 @@ Global
{C01E1407-7FEC-4C1D-B0B4-74D95A317AA6} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{166A16C0-B89F-41AF-956A-235C6CA62C25} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{E066F547-7261-4561-AEFC-E64DBFD874F8} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
- {2F9A6E0C-DE95-4460-96B7-EB72BBEAEE9E} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{014A3DCE-50A6-4774-A4C1-C66EEAB67133} = {BF4C4202-5015-4FBD-80E6-D0F36A06F700}
{17831017-C29C-4A48-B159-849BCE5079FB} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{814F94D6-1413-4ACB-B1B5-A3488CAA1E6B} = {BF4C4202-5015-4FBD-80E6-D0F36A06F700}
@@ -262,7 +260,6 @@ Global
{C100FBA6-4164-4D6A-A532-5984D2B8DCB0} = {BF4C4202-5015-4FBD-80E6-D0F36A06F700}
{8D9FD130-7905-47D8-A25C-7FDEE28EA0E8} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{32C91908-5CAD-4C95-B240-ACBBACAC9476} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
- {7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{6AC51E68-4681-463A-B4B6-BD53517244B2} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637} = {51A614B0-E583-4DD2-AC7D-6A65634582E0}
{AC2503A7-736D-4AE6-9355-CF35D9DF6139} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
@@ -273,6 +270,8 @@ Global
{EDBA7DDC-69AF-4D5B-A8F6-3B508F8CC0FC} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{EAC1B79C-F77D-4DEF-BF53-75E700A301A4} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{7A6CB600-2393-468F-9952-84EC624D57BD} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
+ {4E467A70-6188-4409-B29B-E1E7DD8CDA3C} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
+ {EB77463C-9D06-4AAE-84F0-470988D30DA0} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{9A31C44C-9C51-4D41-B8E5-2864245F877E} = {51A614B0-E583-4DD2-AC7D-6A65634582E0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
diff --git a/MessagePackAnalyzer.schema.json b/MessagePackAnalyzer.schema.json
deleted file mode 100644
index 945e490cc..000000000
--- a/MessagePackAnalyzer.schema.json
+++ /dev/null
@@ -1,61 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-04/schema",
- "title": "MessagePackAnalyzer.json schema",
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "generator": {
- "type": "object",
- "description": "Customizes AOT source generation of formatters for custom types.",
- "additionalProperties": false,
- "properties": {
- "usesMapMode": {
- "type": "boolean",
- "description": "A value indicating whether types will be serialized with their property names as well as their values in a key=value dictionary, as opposed to an array of values.",
- "default": false
- },
- "resolver": {
- "type": "object",
- "description": "Describes the generated resolver.",
- "additionalProperties": false,
- "properties": {
- "public": {
- "type": "boolean",
- "description": "A value indicating whether the generated resolver should be public (as opposed to internal). A public resolver is appropriate when developing a library that may be used by another assembly that needs to aggregate this generated resolver with others.",
- "default": false
- },
- "name": {
- "type": "string",
- "description": "The name to use for the resolver.",
- "default": "GeneratedMessagePackResolver"
- },
- "namespace": {
- "type": "string",
- "description": "The namespace the source generated resolver will be emitted into.",
- "default": "MessagePack"
- }
- }
- },
- "formatters": {
- "type": "object",
- "description": "Customizes aspects of source generated formatters.",
- "additionalProperties": false,
- "properties": {
- "namespace": {
- "type": "string",
- "description": "The root namespace into which formatters are emitted.",
- "default": "Formatters"
- }
- }
- }
- }
- },
- "customFormattedTypes": {
- "type": "array",
- "description": "An array of fully-qualified names of types that are included in serialized object graphs but are assumed to have custom formatters registered already.",
- "items": {
- "type": "string"
- }
- }
- }
-}
diff --git a/README.md b/README.md
index 80f3e3713..a8ef31462 100644
--- a/README.md
+++ b/README.md
@@ -163,25 +163,21 @@ By default, a `MessagePackObject` annotation is required. This can be made optio
The MessagePackAnalyzer package aids with:
-1. Automating definitions for your serializable objects.
1. Produces compiler warnings upon incorrect attribute use, member accessibility, and more.
+1. Automating attributing of your serializable classes and members.
+1. Optionally improving startup time through [AOT formatter generation](#aot).
-
+The first two of these features is demonstrated below:
-If you want to allow a specific custom type (for example, when registering a custom type), put `MessagePackAnalyzer.json` at the project root.
-If using Unity, you should configure Unity to treat this as an `AdditionalFiles` in the C# compiler.
+
-An example `MessagePackAnalyzer.json`:
+Two assembly-level attributes exist to help with mixing in your own custom formatters with the automatically generated ones:
+- `MessagePackKnownFormatterAttribute` - Identifies classes that implement `IMessagePackFormatter`.
+The `T` type argument will _not_ produce an analyzer warning when `T` is used elsewhere in a serializable object.
+When using a source generated resolver, the resolver will refer to this formatter for the appropriate type(s).
+- `MessagePackAssumedFormattableAttribute` - Identifies types that are assumed to have an `IMessagePackFormatter` *somewhere*, and that will be combined within an `IFormatterResolver` at runtime to ensure the specified type can be serialized.
+This attribute will suppress the analyzer warning from using that type although the type does not have a `[MessagePackObject]` attribute on it.
-```json
-{
- "$schema": "https://raw.githubusercontent.com/MessagePack-CSharp/MessagePack-CSharp/develop/MessagePackAnalyzer.schema.json",
- "customFormattedTypes": [
- "MyNamespace.MyClass",
- "MyNamespace.AnotherClass"
- ]
-}
-```
## Built-in supported types
@@ -353,7 +349,10 @@ You can use `[DataContract]` annotations instead of `[MessagePackObject]` ones.
Then `[DataMember(Order = int)]` will behave the same as `[Key(int)]`, `[DataMember(Name = string)]` the same as `[Key(string)]`, and `[DataMember]` the same as `[Key(nameof(member name)]`.
-Using `DataContract`, e.g. in shared libraries, makes your classes/structs independent from MessagePack for C# serialization. However, it is not supported by the analyzers nor in code generation by the `mpc` tool. Also, features like `UnionAttribute`, `MessagePackFormatter`, `SerializationConstructor`, etc can not be used. Due to this, we recommend that you use the specific MessagePack for C# annotations when possible.
+Using `DataContract`, e.g. in shared libraries, makes your classes/structs independent from MessagePack for C# serialization.
+However, it is not supported by the analyzers nor source generator.
+Also, features like `UnionAttribute`, `MessagePackFormatter`, `SerializationConstructor`, etc can not be used.
+Due to this, we recommend that you use the specific MessagePack for C# annotations when possible.
## Serializing readonly/immutable object members (SerializationConstructor)
@@ -1583,37 +1582,50 @@ If you want to share a class between Unity and a server, you can use `SharedProj
By default, MessagePack for C# serializes custom objects by [generating IL](https://learn.microsoft.com/dotnet/api/system.reflection.emit.ilgenerator) on the fly at runtime to create custom, highly tuned formatters for each type.
This code generation has a minor upfront performance cost.
-Because strict-AOT environments such as Xamarin and Unity IL2CPP forbid runtime code generation, MessagePack provides a way for you to run a code generator ahead of time as well.
+
+For faster startup performance or to operate in strict-AOT environments such as Xamarin and Unity IL2CPP that forbid runtime code generation, MessagePack provides a way for you to run a code generator ahead of time as well.
+This "source generation" is provided via a roslyn source generator.
> Note: When using Unity, dynamic code generation only works when targeting .NET Framework 4.x + mono runtime.
For all other Unity targets, AOT is required.
-If you want to avoid the upfront dynamic generation cost or you need to run on Xamarin or Unity, you need AOT code generation.
+Install the source generator via its NuGet package.
+For MSBuild based projects, you can do this within Visual Studio or at the CLI via this command:
```ps1
-dotnet add package MessagePack.SourceGenerator
+dotnet add package MessagePackAnalyzer
```
-Or for Unity, use the source generator that targets the older Roslyn compiler.
-[Setting up a source generator for unity](https://docs.unity3d.com/Manual/roslyn-analyzers.html) is a bit more involved.
+For Unity, using the source generator is a bit more involved.
+Please refer to [Setting up a source generator for unity](https://docs.unity3d.com/Manual/roslyn-analyzers.html).
The unity instructions describe copying the analyzer .dll into your unity project.
-You should get the analyzer/source generator .dll's from the the `MessagePack.SourceGenerator.Unity.zip` file uploaded on our GitHub releases page.
-For each and every .dll in the .zip, be sure to:
+You should get the analyzer/source generator .dll's from the `MessagePackAnalyzer` nuget package and use the DLLs in the `analyzers/roslyn3.8/cs` directory.
+For each and every .dll in that directory, be sure to:
1. Windows only: Unblock each .dll for use by [removing the "Mark of the Web"](doc/mark_of_the_web.png). If the "Unblock" checkbox does not appear, the mark of the web is not present and you may proceed.
1. Add to the unity project as an analyzer.
-The package (or unity .zip file) adds a roslyn Source Generator that produces `IMessagePackFormatter` implementing classes for each of your `[MessagePackObject]` classes.
+You must then define a `partial class` into which the source generator will create your resolver:
+
+```cs
+[GeneratedMessagePackResolver]
+partial class MyResolver
+{
+}
+```
-These formatters are aggregated into a generated `IMessagePackResolver` class named `GeneratedMessagePackResolver`.
-This class will be generated into the `$(RootNamespace)` of your project, or the `MessagePack` namespace if `RootNamespace` is empty or undefined (as in Unity).
+This partial class, combined with at least one `[MessagePackObject]`-annotated type, will result in some members being added to your resolver class.
+These members will _not_ be in your own source file, but they will be emitted during the compilation.
+Visual Studio allows you to see these source generated files through a variety of means.
+These members include the following properties `Instance` and `InstanceWithStandardAotResolver`.
+You can use these members to serialize your object graph.
-Leveraging these formatters at runtime requires that you opt-in, which typically looks like this:
+Leveraging this resolver at runtime requires that you opt-in, which typically looks like this:
```cs
/// Options to use MessagePack with AOT-generated formatters.
private static readonly MessagePackSerializerOptions SerializerOptions = MessagePackSerializerOptions.Standard
- .WithResolver(GeneratedMessagePackResolver.InstanceWithStandardAotResolver);
+ .WithResolver(MyResolver.InstanceWithStandardAotResolver);
// Serialize and deserialize using the AOT option.
byte[] serialized = MessagePackSerializer.Serialize(value, SerializerOptions);
@@ -1631,38 +1643,17 @@ T after = MessagePackSerializer.Deserialize(serialized);
### Customizations
-You can customize the generated source through a `MessagePackAnalyzer.json` file added to the project root directory.
-If using Unity, you should configure Unity to treat this as an `AdditionalFiles` in the C# compiler.
-
-An example `MessagePackAnalyzer.json`:
-
-```json
-{
- "$schema": "https://raw.githubusercontent.com/MessagePack-CSharp/MessagePack-CSharp/develop/MessagePackAnalyzer.schema.json",
- "generator": {
- "resolver": {
- "public": false,
- "name": "GeneratedMessagePackResolver",
- "namespace": ""
- },
- "formatters": {
- "namespace": "Formatters"
- },
- "usesMapMode": false
- },
- "customFormattedTypes": [
- "MyNamespace.MyClass",
- "MyNamespace.AnotherClass"
- ]
-}
-```
-
-The above example mostly sets defaults.
-You can discover all the available settings, their defaults and meanings in a JSON editor that supports JSON schema, or by reviewing [the JSON schema][AnalyzerJsonSchema] yourself.
+You can customize the generated source through properties on the `GeneratedMessagePackResolverAttribute`.
When exposing the generated resolver publicly, consumers outside the library should aggregate the resolver using its `Instance` property, which contains *only* the generated formatters.
The `InstanceWithStandardAotResolver` property is a convenience for callers that will not be aggregating the resolver with those from other libraries, since it aggregates built-in AOT friendly resolvers from the MessagePack library itself.
+Two assembly-level attributes exist to help with mixing in your own custom formatters with the automatically generated ones:
+- `MessagePackKnownFormatterAttribute`
+- `MessagePackAssumedFormattableAttribute`
+
+Learn more about using a mix of your own custom formatters and automatically generated ones in [the Analyzer section](#analyzer).
+
### Unity-specific AOT concerns
Here is the full sample code to register a generated resolver in Unity.
@@ -1716,5 +1707,3 @@ The StreamJsonRpc library is based on [JSON-RPC](https://www.jsonrpc.org/) and i
## How to build
See our [contributor's guide](CONTRIBUTING.md).
-
-[AnalyzerJsonSchema]: https://github.com/MessagePack-CSharp/MessagePack-CSharp/blob/develop/MessagePackAnalyzer.schema.json
diff --git a/doc/MessagePackAnalyzer.json b/doc/MessagePackAnalyzer.json
deleted file mode 100644
index a8e9b33cb..000000000
--- a/doc/MessagePackAnalyzer.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "$schema": "../MessagePackAnalyzer.schema.json",
- "generator": {
- "resolver": {
- "public": false,
- "name": "GeneratedMessagePackResolver",
- "namespace": "MessagePack"
- },
- "formatters": {
- "namespace": "Formatters"
- },
- "usesMapMode": false
- },
- "customFormattedTypes": [
- "MyNamespace.MyClass",
- "MyNamespace.AnotherClass"
- ]
-}
\ No newline at end of file
diff --git a/doc/migrating_v1-v2.md b/doc/migrating_v1-v2.md
new file mode 100644
index 000000000..30bd34e47
--- /dev/null
+++ b/doc/migrating_v1-v2.md
@@ -0,0 +1,393 @@
+# Migrating from MessagePack v1.x to MessagePack v2.x
+
+MessagePack 2.0 contains many breaking changes since the 1.x versions.
+These include both binary and source breaking changes, meaning you may need to update your source code as well as recompile against the 2.x version.
+
+The v1.x version will still be serviced for security fixes, but new features will tend to only be offered in the 2.x versions.
+
+Update your package references from the 1.x version you use to the 2.x version. If your project compiles, you may be done.
+Otherwise work through each compiler error. Some common ones you may face are listed below with suggested fixes.
+
+If you own an application that has a mix of MessagePack consumers and not all of them can be upgraded to v2.x at once, you can offer both MessagePack v1.x and v2.x assemblies with your application so that each user can find the one it needs. [Here is a sample](https://github.com/AArnott/MessagePackDualVersions).
+
+## API changes
+
+### MessagePackSerializerOptions
+
+A new `MessagePackSerializerOptions` class becomes a first class citizen in this library.
+It encapsulates the `IFormatterResolver` that used to be passed around by itself.
+It also includes several other settings that may influence how `MessagePackSerializerOptions` or some of the
+formatters may operate.
+
+Because this new class tends to get saved to public static properties, it is immutable to ensure it can be shared safely.
+Each property `Foo` on the class includes a `WithFoo` method which clones the instance and returns the new instance with just that one property changed.
+
+To support this new options class and avoid unnecessary allocations from the copy-and-mutate methods, many of the popular resolvers now expose a public static `Options` property with the resolver preset to itself. So for example, you may use:
+
+```cs
+var msgpack = MessagePackSerializer.Serialize(objectGraph, StandardResolverAllowPrivate.Options);
+var deserializedGraph = MessagePackSerializer.Deserialize(msgpack, StandardResolverAllowPrivate.Options);
+```
+
+If you want to combine a particular resolver with other options changes (e.g. enabling LZ4 compression), you may do that too:
+
+```cs
+var options = StandardResolverAllowPrivate.Options.WithCompression(MessagePackCompression.Lz4BlockArray);
+var msgpack = MessagePackSerializer.Serialize(objectGraph, options);
+var deserializedGraph = MessagePackSerializer.Deserialize(msgpack, options);
+```
+
+An equivalent options instance can be created manually:
+
+```cs
+var options = MessagePackSerializerOptions.Standard
+ .WithCompression(MessagePackCompression.Lz4BlockArray)
+ .WithResolver(StandardResolverAllowPrivate.Instance);
+```
+
+### MessagePackSerializer class
+
+#### Serialization
+
+Serializing object graphs to msgpack is now based on `IBufferWriter` instead of `ref byte[]`.
+This allows for serializing very large object graphs without repeatedly allocating ever-larger arrays and copying the previously serialized msgpack bytes from the smaller buffer to the larger one.
+`IBufferWriter` can direct the written msgpack bytes directly to a pipe, a file, or anywhere else you choose, allowing you to avoid a buffer copy within your own code as well.
+
+An `IBufferWriter` is always wrapped by the new `MessagePackWriter` struct.
+
+Many overloads of the `Serialize` method exist which ultimately all call the overload that accepts a `MessagePackWriter`.
+
+#### Deserialization
+
+Deserializing msgpack sequences is now much more flexible.
+Instead of deserializing from `byte[]` or `ArraySegment` only, you can deserialize from any `ReadOnlyMemory` or `ReadOnlySequence` instance.
+
+`ReadOnlyMemory` is like `ArraySegment` but more friendly and can refer to contiguous memory anywhere including native pointers. You can pass a `byte[]` or `ArraySegment` in anywhere that `ReadOnlyMemory` is expected and C# will implicitly cast for you (without any buffer copying).
+
+`ReadOnlySequence` allows for deserialization from non-continguously allocated memory, enabling you to deserialize very large msgpack sequences without risking an `OutOfMemoryException` due simply to the inability to find large amounts of free contiguous memory.
+
+Many overloads of the `Deserialize` method exists which ultimately all call the overload that accepts a `MessagePackReader`.
+
+#### Deserializing from a Stream
+
+Deserializing from a `Stream` has changed from v1.x to v2.0. The `readStrict` parameter has been removed and in v2.x
+the `MessagePackSerializer.Deserialize{Async}(Stream)` methods act as if `readStrict: false` in v1.x.
+This works great and is the preferred API to use when the entire `Stream` is expected to contain exactly one
+top-level messagepack structure that you want to deserialize.
+
+For performance reasons, the entire `Stream` is read into memory before deserialization begins.
+If there is more data on the `Stream` than the messagepack structure to be deserialized,
+the deserialization will ignore the excess data, but the excess data wouldn't be on the `Stream`
+any more to be read later.
+
+If the `Stream` is seekable (that is, its `CanSeek` property returns `true`) then after deserialization
+is complete the `Stream` will be repositioned to the first byte after the messagepack data structure
+that was deserialized. This means you'll get the `Stream` back as you might expect it, but only after
+you paid a perf cost of "reading" more data than was necessary to deserialize.
+
+If the `Stream` is *not* seekable (e.g. a network stream) or contains multiple top-level messagepack
+data structures consecutively, MessagePack 2.0 adds a new, more performant way to read each
+messagepack structure. It's analogous to v1.x's `readStrict: true` mode, but is much more performant.
+It comes in the form of the new `MessagePackStreamReader` class, and can be easily used as follows:
+
+```cs
+static async Task> DeserializeListFromStreamAsync(Stream stream, CancellationToken cancellationToken)
+{
+ var dataStructures = new List();
+ using (var streamReader = new MessagePackStreamReader(stream))
+ {
+ while (await streamReader.ReadAsync(cancellationToken) is ReadOnlySequence msgpack)
+ {
+ dataStructures.Add(MessagePackSerializer.Deserialize(msgpack, cancellationToken: cancellationToken));
+ }
+ }
+
+ return dataStructures;
+}
+```
+
+#### Default behavior
+
+The `DefaultResolver` static property has been replaced with the `DefaultOptions` static property.
+Just as with v1.x, in v2.x this static property influences how serialization occurs
+when the value is not explicitly specified when invoking one of the `MessagePackSerializer` methods.
+
+**WARNING**: When developing a simple application where you control all MessagePack-related code it may be safe to rely on this mutable static to control behavior.
+For all other libraries or multi-purpose applications that use `MessagePackSerializer` you should explicitly specify the `MessagePackSerializerOptions` to use with each method invocation to guarantee your code behaves as you expect even when sharing an `AppDomain` or process with other MessagePack users that may change this static property.
+
+#### Non-generic methods
+
+In v1.x non-generic methods for serialization/deserialization were exposed on the nested `MessagePackSerializer.NonGeneric` class.
+In v2.x these overloads are moved to the `MessagePackSerializer` class itself.
+
+The `MessagePackSerializer.Typeless` nested class in v1.x remains in v2.x, but with a modified set of overloads.
+
+#### JSON converting methods
+
+In v1.x the `MessagePackSerializer` class exposed methods both to serialize an object graph to JSON,
+as well as converting between msgpack and JSON. These two translations were very different but were mere overloads of each other.
+In v2.x these methods have been renamed for clarity.
+The methods `ConvertFromJson` and `ConvertToJson` translates between JSON and msgpack binary.
+The method `SerializeToJson` translates an object graph to JSON.
+
+#### LZ4MessagePackSerializer
+
+The `LZ4MessagePackSerializer` class has been removed.
+Instead, use `MessagePackSerializer` and pass in a `MessagePackSerializerOptions` with `WithCompression` set to `MessagePackCompression.Lz4Block` or `MessagePackCompression.Lz4BlockArray`.
+
+For example, make this change:
+
+```diff
+-byte[] buffer = LZ4MessagePackSerializer.Serialize("hi");
++static readonly lz4Options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);
++byte[] buffer = MessagePackSerializer.Serialize("hi", lz4Options);
+```
+
+`Lz4Block` is same as v1 LZ4MessagePackSerializer. `Lz4BlockArray` is new compression mode of v2. Regardless of which Lz4 option is set at the deserialization, both data can be deserialized. For example, when the option is `Lz4BlockArray`, binary data of both `Lz4Block` and `Lz4BlockArray` can be deserialized.
+
+### Thrown exceptions
+
+In v1.x any exception thrown during serialization or deserialization was uncaught and propagated to the application.
+In v2.x all exceptions are caught by the `MessagePackSerializer` and rethrown as an inner exception of `MessagePackSerializationException`.
+This makes it easier to write code to catch exceptions during serialization since you can now catch just one specific type of exception.
+
+### Built-in resolvers
+
+The following resolvers have been *removed*:
+
+| Removed v1.x formatter | v2.x alternative |
+|--|--|
+| `UnsafeBinaryResolver` | `NativeDecimalResolver`, `NativeGuidResolver`
+
+#### CompositeResolver
+
+In v1.x the `CompositeResolver` type could only be used once and mutated a static property.
+In v2.x the `CompositeResolver` type no longer mutates any statics and thus can be used safely by many callers that simply want to aggregate many formatters and/or resolvers into one resolver. This often removes the need for you to define your own `IFormatterResolver`.
+
+For example if you have written a custom formatter and want to use that in addition to what the `StandardResolver` offers, you can easily compose an aggregate resolver like this:
+
+```cs
+var resolver = CompositeResolver.Create(
+ new IMessagePackFormatter[] { MyCustomFormatter.Instance },
+ new IFormatterResolver[] { StandardResolver.Instance }
+);
+var options = MessagePackSerializerOptions.Standard.WithResolver(resolver);
+var msgpack = MessagePackSerializer.Serialize(objectGraph, options);
+var deserializedGraph = MessagePackSerializer.Deserialize(msgpack, options);
+```
+
+### Built-in formatters
+
+The following formatters have been *removed*:
+
+| Removed v1.x formatter | v2.x alternative |
+|--|--|
+| `BinaryDecimalFormatter` | `NativeDecimalFormatter`
+| `BinaryGuidFormatter` | `NativeGuidFormatter`
+| `FourDimentionalArrayFormatter` | `FourDimensionalArrayFormatter`
+| `OldSpecBinaryFormatter` | Use `MessagePackSerializerOptions.OldSpec` or `MessagePackWriter.OldSpec` instead.
+| `OldSpecStringFormatter` | Use `MessagePackSerializerOptions.OldSpec` or `MessagePackWriter.OldSpec` instead.
+| `QeueueFormatter` | `QueueFormatter`
+| `TaskUnitFormatter` | Store values instead of promises
+| `TaskValueFormatter` | Store values instead of promises
+| `ThreeDimentionalArrayFormatter` | `ThreeDimensionalArrayFormatter`
+| `TwoDimentionalArrayFormatter` | `TwoDimensionalArrayFormatter`
+| `ValueTaskFormatter` | Store values instead of promises
+
+A few formatters that remain have changed to remove mutable properties where those formatters may be exposed
+as public static instances. This helps to avoid malfunctions when one MessagePack user changes a static setting
+to suit their need but in a way that conflicts with another MessagePack user within the same process.
+
+#### `TypelessFormatter` changes
+
+The `TypelessFormatter.BindToType` static property has been removed.
+If you were using this property, you can find equivalent functionality in the virtual `Type MessagePackSerializerOptions.LoadType(string typeName)` method. The `TypelessFormatter` will call this method on
+the `MessagePackSerializerOptions` instance passed to it during deserialization.
+
+For example, you can override this virtual method in your own derived type:
+
+```cs
+class LoadTypeCustomizedOptions : MessagePackSerializerOptions
+{
+ internal LoadTypeCustomizedOptions(MessagePackSerializerOptions copyFrom)
+ : base(copyFrom)
+ {
+ }
+
+ internal LoadTypeCustomizedOptions(IFormatterResolver resolver)
+ : base(resolver)
+ {
+ }
+
+ public override Type LoadType(string typeName)
+ {
+ Type type = base.LoadType(typeName);
+ if (type == null)
+ {
+ // custom logic here
+ }
+
+ return type;
+ }
+}
+```
+
+You can then instantiate this options type and pass it to your deserializer:
+
+```cs
+var options = new LoadTypeCustomizedOptions(MessagePackSerializerOptions.Standard);
+T value = MessagePackSerializer.Deserialize(sequence, options);
+```
+
+### Custom formatters
+
+If you have written a custom `IMessagePackFormatter` implementation you will have to adapt to the interface changes and APIs used to implement such a class.
+
+The interface has been changed as described here:
+
+```diff
+ public interface IMessagePackFormatter : IMessagePackFormatter
+ {
+- int Serialize(ref byte[] bytes, int offset, T value, IFormatterResolver formatterResolver);
++ void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options);
+- T Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize);
++ T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options);
+ }
+```
+
+Notice the simpler method signature for each method.
+You no longer have to deal with raw arrays and offsets.
+The `MessagePackBinary` static class from v1.x that a formatter used to write msgpack codes is replaced with `MessagePackWriter` and `MessagePackReader`.
+These two structs include the APIs to write and read msgpack, and they manage the underlying buffers so you no longer need to.
+
+Consider the following v1.x formatter for the `Int16` type:
+
+```cs
+class NullableInt16Formatter : IMessagePackFormatter
+{
+ public int Serialize(ref byte[] bytes, int offset, Int16? value, IFormatterResolver formatterResolver)
+ {
+ if (value == null)
+ {
+ return MessagePackBinary.WriteNil(ref bytes, offset);
+ }
+ else
+ {
+ return MessagePackBinary.WriteInt16(ref bytes, offset, value.Value);
+ }
+ }
+
+ public Int16? Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize)
+ {
+ if (MessagePackBinary.IsNil(bytes, offset))
+ {
+ readSize = 1;
+ return null;
+ }
+ else
+ {
+ return MessagePackBinary.ReadInt16(bytes, offset, out readSize);
+ }
+ }
+}
+```
+
+After migration for v2.x, it looks like this:
+
+```cs
+class NullableInt16Formatter : IMessagePackFormatter
+{
+ public void Serialize(ref MessagePackWriter writer, Int16? value, MessagePackSerializerOptions options)
+ {
+ if (value == null)
+ {
+ writer.WriteNil();
+ }
+ else
+ {
+ writer.Write(value.Value);
+ }
+ }
+
+ public Int16? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
+ {
+ if (reader.TryReadNil())
+ {
+ return default;
+ }
+ else
+ {
+ return reader.ReadInt16();
+ }
+ }
+}
+```
+
+Notice the structure is very similar, but arrays and offsets are no longer necessary.
+The underlying msgpack format is unchanged, allowing code to be upgraded to v2.x while maintaining
+compatibility with a file or network party that uses MessagePack v1.x.
+
+#### Subtle change in method naming
+
+When writing integers, the method name pattern has changed such that although your v1.x->v2.0 code will compile
+it may produce slightly different (and less efficient) msgpack binary than before. Here is the translation table:
+
+|v1.x|v2.x|
+|--|--|
+|`MessagePackBinary.WriteMapHeaderForceMap32Block`|(removed)
+|`MessagePackBinary.WriteArrayHeaderForceArray32Block`|(removed)
+|`MessagePackBinary.WriteByteForceByteBlock`|`MessagePackWriter.WriteUInt8(byte)`
+|`MessagePackBinary.WriteSByteForceSByteBlock`|`MessagePackWriter.WriteInt8(sbyte)`
+|`MessagePackBinary.WriteInt16ForceInt16Block`|`MessagePackWriter.WriteInt16(short)`
+|`MessagePackBinary.WriteInt64ForceInt64Block`|`MessagePackWriter.WriteInt64(long)`
+|`MessagePackBinary.MessagePackBinary.WriteInt32ForceInt32Block`|`MessagePackWriter.WriteInt32(int)`
+|`MessagePackBinary.WriteUInt16ForceUInt16Block`|`MessagePackWriter.WriteUInt16(ushort)`
+|`MessagePackBinary.WriteUInt32ForceUInt32Block`|`MessagePackWriter.WriteUInt32(uint)`
+|`MessagePackBinary.WriteUInt64ForceUInt64Block`|`MessagePackWriter.WriteUInt64(ulong)`
+|`MessagePackBinary.WriteStringForceStr32Block`|(removed)
+|`MessagePackBinary.WriteExtensionFormatHeaderForceExt32Block`|(removed)
+|`MessagePackBinary.WriteMapHeader`|`MessagePackWriter.WriteMapHeader`
+|`MessagePackBinary.WriteArrayHeader`|`MessagePackWriter.WriteArrayHeader`
+|`MessagePackBinary.WriteByte`|`MessagePackWriter.Write(byte)`
+|`MessagePackBinary.WriteBytes`|`MessagePackWriter.Write(byte[])`
+|`MessagePackBinary.WriteSByte`|`MessagePackWriter.Write(sbyte)`
+|`MessagePackBinary.WriteSingle`|`MessagePackWriter.Write(float)`
+|`MessagePackBinary.WriteDouble`|`MessagePackWriter.Write(double)`
+|`MessagePackBinary.WriteInt16`|`MessagePackWriter.Write(short)`
+|`MessagePackBinary.WriteInt32`|`MessagePackWriter.Write(int)`
+|`MessagePackBinary.WriteInt64`|`MessagePackWriter.Write(long)`
+|`MessagePackBinary.WriteUInt16`|`MessagePackWriter.Write(ushort)`
+|`MessagePackBinary.WriteUInt32`|`MessagePackWriter.Write(uint)`
+|`MessagePackBinary.WriteUInt64`|`MessagePackWriter.Write(ulong)`
+|`MessagePackBinary.WriteChar`|`MessagePackWriter.Write(char)`
+|`MessagePackBinary.WriteStringBytes`|`MessagePackWriter.WriteString(ReadOnlySpan)`
+|`MessagePackBinary.WriteString`|`MessagePackWriter.Write(string)`
+|`MessagePackBinary.WriteExtensionFormatHeader`|`MessagePackWriter.WriteExtensionFormatHeader`
+|`MessagePackBinary.WriteExtensionFormat`|`MessagePackWriter.WriteExtensionFormat`
+|`MessagePackBinary.WriteDateTime`|`MessagePackWriter.Write(DateTime)` ([notes](#DateTime))
+
+The essence here is that you can typically just call `MessagePackWriter.Write(*)`
+for primitive types and the most efficient msgpack binary will be written out.
+You only should call the explicit `WriteX(x)` methods if you need to force a particular
+(fixed length) format of a value to be written out.
+
+As for the integer *reading* methods, these are much more interchangeable than in v1.x.
+You can call *any* `ReadInt*` or `ReadUInt*` method and it will successfully read an integer
+value and fit it into the desired return type so long as the value doesn't overflow.
+So for example you can call `Write(byte)` and later read the value with `ReadInt32()`.
+You can even call `Write(long)` and later read it with `ReadByte()` and it will work
+so long as the actual value fits inside a `byte`.
+An `OverflowException` is thrown if the integer value exceeds the max or min value
+that can be stored by the required return type.
+
+## Behavioral changes
+
+### DateTime
+
+When writing out `DateTime` v1.x would *always* call `DateTime.ToUniversalTime()` before serializing the value.
+In v2.x [we only call this method if `DateTime.Kind == DateTimeKind.Local`](https://github.com/neuecc/MessagePack-CSharp/pull/520/files).
+The impact of this is that if you were writing `DateTimeKind.Unspecified` the serialized value will no longer be changed
+under some unjustified assumption that the underlying value was `Local`.
+Your should specify `DateTimeKind` explicitly for all your `DateTime` values.
+When upgrading to MessagePack v2.x this is a breaking change if your `Unspecified` values actually represented the `Local`
+time zone and needed the conversion.
diff --git a/doc/migrating_v2-v3.md b/doc/migrating_v2-v3.md
new file mode 100644
index 000000000..5fd5f9206
--- /dev/null
+++ b/doc/migrating_v2-v3.md
@@ -0,0 +1,25 @@
+# Migrating from MessagePack v2 to v3
+
+## Breaking Changes
+
+- `MessagePackAnalyzer.json` is no longer used to configure the analyzer.
+ Use `GeneratedMessagePackResolverAttribute`, `MessagePackKnownFormatterAttribute` and `MessagePackAssumedFormattableAttribute` instead.
+- The `mpc` CLI tool is no longer used to generate ahead-of-time (AOT) formatters and resolver.
+ Use the source generator included in the `MessagePackAnalyzer` nuget package instead.
+- Unity users:
+ - Use NuGetForUnity to acquire the `MessagePack` nuget package instead of acquiring source code via the .zip file on our Releases page.
+ - Unity 2021.3 is no longer supported (TODO: what version _is_ supported?)
+
+## Adapting to breaking changes
+
+### Migrate your `MessagePackAnalyzer.json` file
+
+1. Add `[assembly: MessagePackAssumedFormattable(typeof(MyType1))]` to your project for each type that appears inside your `MessagePackAnalyzer.json` file.
+1. Delete the `MessagePackAnalyzer.json` file.
+
+### Migrate from `mpc`
+
+1. Remove any scripts that invoked `mpc` from your build.
+1. Follow the instructions in [the AOT section of the README](../README.md#aot) to create a source generated resolver (with formatters).
+
+Be sure to build with .NET SDK 6.0 or later.
diff --git a/doc/migration.md b/doc/migration.md
index 30bd34e47..a815c74f1 100644
--- a/doc/migration.md
+++ b/doc/migration.md
@@ -1,393 +1,4 @@
-# Migrating from MessagePack v1.x to MessagePack v2.x
+# Migration instructions
-MessagePack 2.0 contains many breaking changes since the 1.x versions.
-These include both binary and source breaking changes, meaning you may need to update your source code as well as recompile against the 2.x version.
-
-The v1.x version will still be serviced for security fixes, but new features will tend to only be offered in the 2.x versions.
-
-Update your package references from the 1.x version you use to the 2.x version. If your project compiles, you may be done.
-Otherwise work through each compiler error. Some common ones you may face are listed below with suggested fixes.
-
-If you own an application that has a mix of MessagePack consumers and not all of them can be upgraded to v2.x at once, you can offer both MessagePack v1.x and v2.x assemblies with your application so that each user can find the one it needs. [Here is a sample](https://github.com/AArnott/MessagePackDualVersions).
-
-## API changes
-
-### MessagePackSerializerOptions
-
-A new `MessagePackSerializerOptions` class becomes a first class citizen in this library.
-It encapsulates the `IFormatterResolver` that used to be passed around by itself.
-It also includes several other settings that may influence how `MessagePackSerializerOptions` or some of the
-formatters may operate.
-
-Because this new class tends to get saved to public static properties, it is immutable to ensure it can be shared safely.
-Each property `Foo` on the class includes a `WithFoo` method which clones the instance and returns the new instance with just that one property changed.
-
-To support this new options class and avoid unnecessary allocations from the copy-and-mutate methods, many of the popular resolvers now expose a public static `Options` property with the resolver preset to itself. So for example, you may use:
-
-```cs
-var msgpack = MessagePackSerializer.Serialize(objectGraph, StandardResolverAllowPrivate.Options);
-var deserializedGraph = MessagePackSerializer.Deserialize(msgpack, StandardResolverAllowPrivate.Options);
-```
-
-If you want to combine a particular resolver with other options changes (e.g. enabling LZ4 compression), you may do that too:
-
-```cs
-var options = StandardResolverAllowPrivate.Options.WithCompression(MessagePackCompression.Lz4BlockArray);
-var msgpack = MessagePackSerializer.Serialize(objectGraph, options);
-var deserializedGraph = MessagePackSerializer.Deserialize(msgpack, options);
-```
-
-An equivalent options instance can be created manually:
-
-```cs
-var options = MessagePackSerializerOptions.Standard
- .WithCompression(MessagePackCompression.Lz4BlockArray)
- .WithResolver(StandardResolverAllowPrivate.Instance);
-```
-
-### MessagePackSerializer class
-
-#### Serialization
-
-Serializing object graphs to msgpack is now based on `IBufferWriter` instead of `ref byte[]`.
-This allows for serializing very large object graphs without repeatedly allocating ever-larger arrays and copying the previously serialized msgpack bytes from the smaller buffer to the larger one.
-`IBufferWriter` can direct the written msgpack bytes directly to a pipe, a file, or anywhere else you choose, allowing you to avoid a buffer copy within your own code as well.
-
-An `IBufferWriter` is always wrapped by the new `MessagePackWriter` struct.
-
-Many overloads of the `Serialize` method exist which ultimately all call the overload that accepts a `MessagePackWriter`.
-
-#### Deserialization
-
-Deserializing msgpack sequences is now much more flexible.
-Instead of deserializing from `byte[]` or `ArraySegment` only, you can deserialize from any `ReadOnlyMemory` or `ReadOnlySequence` instance.
-
-`ReadOnlyMemory` is like `ArraySegment` but more friendly and can refer to contiguous memory anywhere including native pointers. You can pass a `byte[]` or `ArraySegment` in anywhere that `ReadOnlyMemory` is expected and C# will implicitly cast for you (without any buffer copying).
-
-`ReadOnlySequence` allows for deserialization from non-continguously allocated memory, enabling you to deserialize very large msgpack sequences without risking an `OutOfMemoryException` due simply to the inability to find large amounts of free contiguous memory.
-
-Many overloads of the `Deserialize` method exists which ultimately all call the overload that accepts a `MessagePackReader`.
-
-#### Deserializing from a Stream
-
-Deserializing from a `Stream` has changed from v1.x to v2.0. The `readStrict` parameter has been removed and in v2.x
-the `MessagePackSerializer.Deserialize{Async}(Stream)` methods act as if `readStrict: false` in v1.x.
-This works great and is the preferred API to use when the entire `Stream` is expected to contain exactly one
-top-level messagepack structure that you want to deserialize.
-
-For performance reasons, the entire `Stream` is read into memory before deserialization begins.
-If there is more data on the `Stream` than the messagepack structure to be deserialized,
-the deserialization will ignore the excess data, but the excess data wouldn't be on the `Stream`
-any more to be read later.
-
-If the `Stream` is seekable (that is, its `CanSeek` property returns `true`) then after deserialization
-is complete the `Stream` will be repositioned to the first byte after the messagepack data structure
-that was deserialized. This means you'll get the `Stream` back as you might expect it, but only after
-you paid a perf cost of "reading" more data than was necessary to deserialize.
-
-If the `Stream` is *not* seekable (e.g. a network stream) or contains multiple top-level messagepack
-data structures consecutively, MessagePack 2.0 adds a new, more performant way to read each
-messagepack structure. It's analogous to v1.x's `readStrict: true` mode, but is much more performant.
-It comes in the form of the new `MessagePackStreamReader` class, and can be easily used as follows:
-
-```cs
-static async Task> DeserializeListFromStreamAsync(Stream stream, CancellationToken cancellationToken)
-{
- var dataStructures = new List();
- using (var streamReader = new MessagePackStreamReader(stream))
- {
- while (await streamReader.ReadAsync(cancellationToken) is ReadOnlySequence msgpack)
- {
- dataStructures.Add(MessagePackSerializer.Deserialize(msgpack, cancellationToken: cancellationToken));
- }
- }
-
- return dataStructures;
-}
-```
-
-#### Default behavior
-
-The `DefaultResolver` static property has been replaced with the `DefaultOptions` static property.
-Just as with v1.x, in v2.x this static property influences how serialization occurs
-when the value is not explicitly specified when invoking one of the `MessagePackSerializer` methods.
-
-**WARNING**: When developing a simple application where you control all MessagePack-related code it may be safe to rely on this mutable static to control behavior.
-For all other libraries or multi-purpose applications that use `MessagePackSerializer` you should explicitly specify the `MessagePackSerializerOptions` to use with each method invocation to guarantee your code behaves as you expect even when sharing an `AppDomain` or process with other MessagePack users that may change this static property.
-
-#### Non-generic methods
-
-In v1.x non-generic methods for serialization/deserialization were exposed on the nested `MessagePackSerializer.NonGeneric` class.
-In v2.x these overloads are moved to the `MessagePackSerializer` class itself.
-
-The `MessagePackSerializer.Typeless` nested class in v1.x remains in v2.x, but with a modified set of overloads.
-
-#### JSON converting methods
-
-In v1.x the `MessagePackSerializer` class exposed methods both to serialize an object graph to JSON,
-as well as converting between msgpack and JSON. These two translations were very different but were mere overloads of each other.
-In v2.x these methods have been renamed for clarity.
-The methods `ConvertFromJson` and `ConvertToJson` translates between JSON and msgpack binary.
-The method `SerializeToJson` translates an object graph to JSON.
-
-#### LZ4MessagePackSerializer
-
-The `LZ4MessagePackSerializer` class has been removed.
-Instead, use `MessagePackSerializer` and pass in a `MessagePackSerializerOptions` with `WithCompression` set to `MessagePackCompression.Lz4Block` or `MessagePackCompression.Lz4BlockArray`.
-
-For example, make this change:
-
-```diff
--byte[] buffer = LZ4MessagePackSerializer.Serialize("hi");
-+static readonly lz4Options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);
-+byte[] buffer = MessagePackSerializer.Serialize("hi", lz4Options);
-```
-
-`Lz4Block` is same as v1 LZ4MessagePackSerializer. `Lz4BlockArray` is new compression mode of v2. Regardless of which Lz4 option is set at the deserialization, both data can be deserialized. For example, when the option is `Lz4BlockArray`, binary data of both `Lz4Block` and `Lz4BlockArray` can be deserialized.
-
-### Thrown exceptions
-
-In v1.x any exception thrown during serialization or deserialization was uncaught and propagated to the application.
-In v2.x all exceptions are caught by the `MessagePackSerializer` and rethrown as an inner exception of `MessagePackSerializationException`.
-This makes it easier to write code to catch exceptions during serialization since you can now catch just one specific type of exception.
-
-### Built-in resolvers
-
-The following resolvers have been *removed*:
-
-| Removed v1.x formatter | v2.x alternative |
-|--|--|
-| `UnsafeBinaryResolver` | `NativeDecimalResolver`, `NativeGuidResolver`
-
-#### CompositeResolver
-
-In v1.x the `CompositeResolver` type could only be used once and mutated a static property.
-In v2.x the `CompositeResolver` type no longer mutates any statics and thus can be used safely by many callers that simply want to aggregate many formatters and/or resolvers into one resolver. This often removes the need for you to define your own `IFormatterResolver`.
-
-For example if you have written a custom formatter and want to use that in addition to what the `StandardResolver` offers, you can easily compose an aggregate resolver like this:
-
-```cs
-var resolver = CompositeResolver.Create(
- new IMessagePackFormatter[] { MyCustomFormatter.Instance },
- new IFormatterResolver[] { StandardResolver.Instance }
-);
-var options = MessagePackSerializerOptions.Standard.WithResolver(resolver);
-var msgpack = MessagePackSerializer.Serialize(objectGraph, options);
-var deserializedGraph = MessagePackSerializer.Deserialize(msgpack, options);
-```
-
-### Built-in formatters
-
-The following formatters have been *removed*:
-
-| Removed v1.x formatter | v2.x alternative |
-|--|--|
-| `BinaryDecimalFormatter` | `NativeDecimalFormatter`
-| `BinaryGuidFormatter` | `NativeGuidFormatter`
-| `FourDimentionalArrayFormatter` | `FourDimensionalArrayFormatter`
-| `OldSpecBinaryFormatter` | Use `MessagePackSerializerOptions.OldSpec` or `MessagePackWriter.OldSpec` instead.
-| `OldSpecStringFormatter` | Use `MessagePackSerializerOptions.OldSpec` or `MessagePackWriter.OldSpec` instead.
-| `QeueueFormatter` | `QueueFormatter`
-| `TaskUnitFormatter` | Store values instead of promises
-| `TaskValueFormatter` | Store values instead of promises
-| `ThreeDimentionalArrayFormatter` | `ThreeDimensionalArrayFormatter`
-| `TwoDimentionalArrayFormatter` | `TwoDimensionalArrayFormatter`
-| `ValueTaskFormatter` | Store values instead of promises
-
-A few formatters that remain have changed to remove mutable properties where those formatters may be exposed
-as public static instances. This helps to avoid malfunctions when one MessagePack user changes a static setting
-to suit their need but in a way that conflicts with another MessagePack user within the same process.
-
-#### `TypelessFormatter` changes
-
-The `TypelessFormatter.BindToType` static property has been removed.
-If you were using this property, you can find equivalent functionality in the virtual `Type MessagePackSerializerOptions.LoadType(string typeName)` method. The `TypelessFormatter` will call this method on
-the `MessagePackSerializerOptions` instance passed to it during deserialization.
-
-For example, you can override this virtual method in your own derived type:
-
-```cs
-class LoadTypeCustomizedOptions : MessagePackSerializerOptions
-{
- internal LoadTypeCustomizedOptions(MessagePackSerializerOptions copyFrom)
- : base(copyFrom)
- {
- }
-
- internal LoadTypeCustomizedOptions(IFormatterResolver resolver)
- : base(resolver)
- {
- }
-
- public override Type LoadType(string typeName)
- {
- Type type = base.LoadType(typeName);
- if (type == null)
- {
- // custom logic here
- }
-
- return type;
- }
-}
-```
-
-You can then instantiate this options type and pass it to your deserializer:
-
-```cs
-var options = new LoadTypeCustomizedOptions(MessagePackSerializerOptions.Standard);
-T value = MessagePackSerializer.Deserialize(sequence, options);
-```
-
-### Custom formatters
-
-If you have written a custom `IMessagePackFormatter` implementation you will have to adapt to the interface changes and APIs used to implement such a class.
-
-The interface has been changed as described here:
-
-```diff
- public interface IMessagePackFormatter : IMessagePackFormatter
- {
-- int Serialize(ref byte[] bytes, int offset, T value, IFormatterResolver formatterResolver);
-+ void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options);
-- T Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize);
-+ T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options);
- }
-```
-
-Notice the simpler method signature for each method.
-You no longer have to deal with raw arrays and offsets.
-The `MessagePackBinary` static class from v1.x that a formatter used to write msgpack codes is replaced with `MessagePackWriter` and `MessagePackReader`.
-These two structs include the APIs to write and read msgpack, and they manage the underlying buffers so you no longer need to.
-
-Consider the following v1.x formatter for the `Int16` type:
-
-```cs
-class NullableInt16Formatter : IMessagePackFormatter
-{
- public int Serialize(ref byte[] bytes, int offset, Int16? value, IFormatterResolver formatterResolver)
- {
- if (value == null)
- {
- return MessagePackBinary.WriteNil(ref bytes, offset);
- }
- else
- {
- return MessagePackBinary.WriteInt16(ref bytes, offset, value.Value);
- }
- }
-
- public Int16? Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize)
- {
- if (MessagePackBinary.IsNil(bytes, offset))
- {
- readSize = 1;
- return null;
- }
- else
- {
- return MessagePackBinary.ReadInt16(bytes, offset, out readSize);
- }
- }
-}
-```
-
-After migration for v2.x, it looks like this:
-
-```cs
-class NullableInt16Formatter : IMessagePackFormatter
-{
- public void Serialize(ref MessagePackWriter writer, Int16? value, MessagePackSerializerOptions options)
- {
- if (value == null)
- {
- writer.WriteNil();
- }
- else
- {
- writer.Write(value.Value);
- }
- }
-
- public Int16? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
- {
- if (reader.TryReadNil())
- {
- return default;
- }
- else
- {
- return reader.ReadInt16();
- }
- }
-}
-```
-
-Notice the structure is very similar, but arrays and offsets are no longer necessary.
-The underlying msgpack format is unchanged, allowing code to be upgraded to v2.x while maintaining
-compatibility with a file or network party that uses MessagePack v1.x.
-
-#### Subtle change in method naming
-
-When writing integers, the method name pattern has changed such that although your v1.x->v2.0 code will compile
-it may produce slightly different (and less efficient) msgpack binary than before. Here is the translation table:
-
-|v1.x|v2.x|
-|--|--|
-|`MessagePackBinary.WriteMapHeaderForceMap32Block`|(removed)
-|`MessagePackBinary.WriteArrayHeaderForceArray32Block`|(removed)
-|`MessagePackBinary.WriteByteForceByteBlock`|`MessagePackWriter.WriteUInt8(byte)`
-|`MessagePackBinary.WriteSByteForceSByteBlock`|`MessagePackWriter.WriteInt8(sbyte)`
-|`MessagePackBinary.WriteInt16ForceInt16Block`|`MessagePackWriter.WriteInt16(short)`
-|`MessagePackBinary.WriteInt64ForceInt64Block`|`MessagePackWriter.WriteInt64(long)`
-|`MessagePackBinary.MessagePackBinary.WriteInt32ForceInt32Block`|`MessagePackWriter.WriteInt32(int)`
-|`MessagePackBinary.WriteUInt16ForceUInt16Block`|`MessagePackWriter.WriteUInt16(ushort)`
-|`MessagePackBinary.WriteUInt32ForceUInt32Block`|`MessagePackWriter.WriteUInt32(uint)`
-|`MessagePackBinary.WriteUInt64ForceUInt64Block`|`MessagePackWriter.WriteUInt64(ulong)`
-|`MessagePackBinary.WriteStringForceStr32Block`|(removed)
-|`MessagePackBinary.WriteExtensionFormatHeaderForceExt32Block`|(removed)
-|`MessagePackBinary.WriteMapHeader`|`MessagePackWriter.WriteMapHeader`
-|`MessagePackBinary.WriteArrayHeader`|`MessagePackWriter.WriteArrayHeader`
-|`MessagePackBinary.WriteByte`|`MessagePackWriter.Write(byte)`
-|`MessagePackBinary.WriteBytes`|`MessagePackWriter.Write(byte[])`
-|`MessagePackBinary.WriteSByte`|`MessagePackWriter.Write(sbyte)`
-|`MessagePackBinary.WriteSingle`|`MessagePackWriter.Write(float)`
-|`MessagePackBinary.WriteDouble`|`MessagePackWriter.Write(double)`
-|`MessagePackBinary.WriteInt16`|`MessagePackWriter.Write(short)`
-|`MessagePackBinary.WriteInt32`|`MessagePackWriter.Write(int)`
-|`MessagePackBinary.WriteInt64`|`MessagePackWriter.Write(long)`
-|`MessagePackBinary.WriteUInt16`|`MessagePackWriter.Write(ushort)`
-|`MessagePackBinary.WriteUInt32`|`MessagePackWriter.Write(uint)`
-|`MessagePackBinary.WriteUInt64`|`MessagePackWriter.Write(ulong)`
-|`MessagePackBinary.WriteChar`|`MessagePackWriter.Write(char)`
-|`MessagePackBinary.WriteStringBytes`|`MessagePackWriter.WriteString(ReadOnlySpan)`
-|`MessagePackBinary.WriteString`|`MessagePackWriter.Write(string)`
-|`MessagePackBinary.WriteExtensionFormatHeader`|`MessagePackWriter.WriteExtensionFormatHeader`
-|`MessagePackBinary.WriteExtensionFormat`|`MessagePackWriter.WriteExtensionFormat`
-|`MessagePackBinary.WriteDateTime`|`MessagePackWriter.Write(DateTime)` ([notes](#DateTime))
-
-The essence here is that you can typically just call `MessagePackWriter.Write(*)`
-for primitive types and the most efficient msgpack binary will be written out.
-You only should call the explicit `WriteX(x)` methods if you need to force a particular
-(fixed length) format of a value to be written out.
-
-As for the integer *reading* methods, these are much more interchangeable than in v1.x.
-You can call *any* `ReadInt*` or `ReadUInt*` method and it will successfully read an integer
-value and fit it into the desired return type so long as the value doesn't overflow.
-So for example you can call `Write(byte)` and later read the value with `ReadInt32()`.
-You can even call `Write(long)` and later read it with `ReadByte()` and it will work
-so long as the actual value fits inside a `byte`.
-An `OverflowException` is thrown if the integer value exceeds the max or min value
-that can be stored by the required return type.
-
-## Behavioral changes
-
-### DateTime
-
-When writing out `DateTime` v1.x would *always* call `DateTime.ToUniversalTime()` before serializing the value.
-In v2.x [we only call this method if `DateTime.Kind == DateTimeKind.Local`](https://github.com/neuecc/MessagePack-CSharp/pull/520/files).
-The impact of this is that if you were writing `DateTimeKind.Unspecified` the serialized value will no longer be changed
-under some unjustified assumption that the underlying value was `Local`.
-Your should specify `DateTimeKind` explicitly for all your `DateTime` values.
-When upgrading to MessagePack v2.x this is a breaking change if your `Unspecified` values actually represented the `Local`
-time zone and needed the conversion.
+- [Migrating from MessagePack v1 to v2](migrating_v1-v2.md)
+- [Migrating from MessagePack v2 to v3](migrating_v2-v3.md)
diff --git a/tests/MessagePack.SourceGenerator.Tests/Verifiers/ReferenceHelper.cs b/sandbox/Sandbox/GeneratedMessagePackResolver.cs
similarity index 50%
rename from tests/MessagePack.SourceGenerator.Tests/Verifiers/ReferenceHelper.cs
rename to sandbox/Sandbox/GeneratedMessagePackResolver.cs
index d52a37c65..85ed11ef8 100644
--- a/tests/MessagePack.SourceGenerator.Tests/Verifiers/ReferenceHelper.cs
+++ b/sandbox/Sandbox/GeneratedMessagePackResolver.cs
@@ -1,9 +1,11 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.CodeAnalysis.Testing;
+using MessagePack;
-internal static class ReferenceHelper
+namespace Sandbox;
+
+[GeneratedMessagePackResolver]
+public partial class GeneratedMessagePackResolver
{
- public static ReferenceAssemblies DefaultReferences = ReferenceAssemblies.Net.Net70;
}
diff --git a/sandbox/Sandbox/MessagePackAnalyzer.json b/sandbox/Sandbox/MessagePackAnalyzer.json
deleted file mode 100644
index 1fd8e9c3a..000000000
--- a/sandbox/Sandbox/MessagePackAnalyzer.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "$schema": "../../MessagePackAnalyzer.schema.json",
- "generator": {
- "resolver": {
- "public": true
- }
- }
-}
\ No newline at end of file
diff --git a/sandbox/SharedData/GeneratedMessagePackResolver.cs b/sandbox/SharedData/GeneratedMessagePackResolver.cs
new file mode 100644
index 000000000..cea1a3a1b
--- /dev/null
+++ b/sandbox/SharedData/GeneratedMessagePackResolver.cs
@@ -0,0 +1,11 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using MessagePack;
+
+namespace SharedData;
+
+[GeneratedMessagePackResolver]
+public partial class GeneratedMessagePackResolver
+{
+}
diff --git a/sandbox/SharedData/MessagePackAnalyzer.json b/sandbox/SharedData/MessagePackAnalyzer.json
deleted file mode 100644
index 1fd8e9c3a..000000000
--- a/sandbox/SharedData/MessagePackAnalyzer.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "$schema": "../../MessagePackAnalyzer.schema.json",
- "generator": {
- "resolver": {
- "public": true
- }
- }
-}
\ No newline at end of file
diff --git a/src/MessagePack.Analyzers.CodeFixes.Unity/MessagePack.Analyzers.CodeFixes.Unity.csproj b/src/MessagePack.Analyzers.CodeFixes.Unity/MessagePack.Analyzers.CodeFixes.Unity.csproj
new file mode 100644
index 000000000..3696db70e
--- /dev/null
+++ b/src/MessagePack.Analyzers.CodeFixes.Unity/MessagePack.Analyzers.CodeFixes.Unity.csproj
@@ -0,0 +1,22 @@
+
+
+ netstandard2.0
+ MessagePack.Analyzers
+ false
+ enable
+
+ $(CodeAnalysisVersionForUnity)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/MessagePack.Analyzers/version.json b/src/MessagePack.Analyzers.CodeFixes.Unity/version.json
similarity index 100%
rename from src/MessagePack.Analyzers/version.json
rename to src/MessagePack.Analyzers.CodeFixes.Unity/version.json
diff --git a/src/MessagePack.Analyzers.CodeFixes/MessagePackCodeFixProvider.cs b/src/MessagePack.Analyzers.CodeFixes/CodeFixes/MessagePackCodeFixProvider.cs
similarity index 87%
rename from src/MessagePack.Analyzers.CodeFixes/MessagePackCodeFixProvider.cs
rename to src/MessagePack.Analyzers.CodeFixes/CodeFixes/MessagePackCodeFixProvider.cs
index fcbe2ffe9..428f711aa 100644
--- a/src/MessagePack.Analyzers.CodeFixes/MessagePackCodeFixProvider.cs
+++ b/src/MessagePack.Analyzers.CodeFixes/CodeFixes/MessagePackCodeFixProvider.cs
@@ -7,6 +7,8 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MessagePack.SourceGenerator;
+using MessagePack.SourceGenerator.Analyzers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
@@ -14,7 +16,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
-namespace MessagePack.Analyzers;
+namespace MessagePack.Analyzers.CodeFixes;
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MessagePackCodeFixProvider)), Shared]
public class MessagePackCodeFixProvider : CodeFixProvider
@@ -42,17 +44,17 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
return;
}
- SemanticModel? model = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
+ var model = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
if (model is null)
{
return;
}
- SyntaxNode targetNode = root.FindNode(context.Span);
- TypeInfo myTypeInfo = model.GetTypeInfo(targetNode, context.CancellationToken);
+ var targetNode = root.FindNode(context.Span);
+ var myTypeInfo = model.GetTypeInfo(targetNode, context.CancellationToken);
- string? typeName = context.Diagnostics[0]?.Properties.GetValueOrDefault("type", null);
- INamedTypeSymbol? namedSymbol =
+ var typeName = context.Diagnostics[0]?.Properties.GetValueOrDefault("type", null);
+ var namedSymbol =
myTypeInfo.Type as INamedTypeSymbol ??
(typeName is not null ? model.Compilation.GetTypeByMetadataName(typeName.Replace("global::", string.Empty)) : null);
@@ -61,7 +63,7 @@ myTypeInfo.Type as INamedTypeSymbol ??
var property = targetNode as PropertyDeclarationSyntax;
var field = targetNode as FieldDeclarationSyntax;
var dec = targetNode as VariableDeclaratorSyntax;
- IdentifierNameSyntax? identifierName = targetNode as IdentifierNameSyntax;
+ var identifierName = targetNode as IdentifierNameSyntax;
ITypeSymbol? targetType = null;
if (property == null && field == null)
@@ -96,13 +98,13 @@ myTypeInfo.Type as INamedTypeSymbol ??
{
if (context.Diagnostics[0].Id == MsgPack00xMessagePackAnalyzer.TypeMustBeMessagePackObject.Id)
{
- targetType = (property != null)
+ targetType = property != null
? (model.GetDeclaredSymbol(property) as IPropertySymbol)?.Type
: (model.GetDeclaredSymbol(field!) as IFieldSymbol)?.Type;
}
else
{
- targetType = (property != null)
+ targetType = property != null
? (model.GetDeclaredSymbol(property) as IPropertySymbol)?.ContainingType
: (model.GetDeclaredSymbol(field!) as IFieldSymbol)?.ContainingType;
}
@@ -134,7 +136,7 @@ private static async Task AddKeyAttributeAsync(Document document, INam
{
var solutionEditor = new SolutionEditor(document.Project.Solution);
- ISymbol[] targets = type.GetAllMembers()
+ var targets = type.GetAllMembers()
.Where(x => x.Kind == SymbolKind.Property || x.Kind == SymbolKind.Field)
.Where(x => x.GetAttributes().FindAttributeShortName(MsgPack00xMessagePackAnalyzer.IgnoreShortName) == null && x.GetAttributes().FindAttributeShortName(MsgPack00xMessagePackAnalyzer.IgnoreDataMemberShortName) == null)
.Where(x => !x.IsStatic)
@@ -159,11 +161,11 @@ private static async Task AddKeyAttributeAsync(Document document, INam
.DefaultIfEmpty(-1) // if empty, start from zero.
.Max() + 1;
- foreach (ISymbol member in targets)
+ foreach (var member in targets)
{
if (!member.IsImplicitlyDeclared && member.GetAttributes().FindAttributeShortName(MsgPack00xMessagePackAnalyzer.KeyAttributeShortName) is null)
{
- SyntaxNode node = await member.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
+ var node = await member.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
var documentEditor = await solutionEditor.GetDocumentEditorAsync(document.Project.Solution.GetDocumentId(node.SyntaxTree), cancellationToken).ConfigureAwait(false);
var syntaxGenerator = SyntaxGenerator.GetGenerator(documentEditor.OriginalDocument);
documentEditor.AddAttribute(node, syntaxGenerator.Attribute("MessagePack.KeyAttribute", syntaxGenerator.LiteralExpression(startOrder++)));
@@ -172,7 +174,7 @@ private static async Task AddKeyAttributeAsync(Document document, INam
if (type.GetAttributes().FindAttributeShortName(MsgPack00xMessagePackAnalyzer.MessagePackObjectAttributeShortName) == null)
{
- SyntaxNode node = await type.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
+ var node = await type.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
var documentEditor = await solutionEditor.GetDocumentEditorAsync(document.Project.Solution.GetDocumentId(node.SyntaxTree), cancellationToken).ConfigureAwait(false);
var syntaxGenerator = SyntaxGenerator.GetGenerator(documentEditor.OriginalDocument);
documentEditor.AddAttribute(node, syntaxGenerator.Attribute("MessagePack.MessagePackObject"));
diff --git a/src/MessagePack.Analyzers.CodeFixes/MessagePack.Analyzers.CodeFixes.csproj b/src/MessagePack.Analyzers.CodeFixes/MessagePack.Analyzers.CodeFixes.csproj
index a82577255..d43c3b435 100644
--- a/src/MessagePack.Analyzers.CodeFixes/MessagePack.Analyzers.CodeFixes.csproj
+++ b/src/MessagePack.Analyzers.CodeFixes/MessagePack.Analyzers.CodeFixes.csproj
@@ -2,43 +2,15 @@
netstandard2.0
MessagePack.Analyzers
+ false
enable
- MessagePackAnalyzer
- Analyzer of MessagePack for C#, verify rule for [MessagePackObject] and code fix for [Key].
- MsgPack;MessagePack;Serialization;Formatter;Analyzer
- false
- $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs
- true
- false
- true
-
- $(CodeAnalysisVersionForUnity)
+ $(MicrosoftCodeAnalysisVersion)
-
-
-
-
-
-
-
- all
-
-
-
+
-
-
-
-
-
-
-
-
diff --git a/src/MessagePack.Analyzers.CodeFixes/tools/install.ps1 b/src/MessagePack.Analyzers.CodeFixes/tools/install.ps1
deleted file mode 100644
index 9e3fbbf48..000000000
--- a/src/MessagePack.Analyzers.CodeFixes/tools/install.ps1
+++ /dev/null
@@ -1,58 +0,0 @@
-param($installPath, $toolsPath, $package, $project)
-
-if($project.Object.SupportsPackageDependencyResolution)
-{
- if($project.Object.SupportsPackageDependencyResolution())
- {
- # Do not install analyzers via install.ps1, instead let the project system handle it.
- return
- }
-}
-
-$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
-
-foreach($analyzersPath in $analyzersPaths)
-{
- if (Test-Path $analyzersPath)
- {
- # Install the language agnostic analyzers.
- foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
- {
- if($project.Object.AnalyzerReferences)
- {
- $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
- }
- }
- }
-}
-
-# $project.Type gives the language name like (C# or VB.NET)
-$languageFolder = ""
-if($project.Type -eq "C#")
-{
- $languageFolder = "cs"
-}
-if($project.Type -eq "VB.NET")
-{
- $languageFolder = "vb"
-}
-if($languageFolder -eq "")
-{
- return
-}
-
-foreach($analyzersPath in $analyzersPaths)
-{
- # Install language specific analyzers.
- $languageAnalyzersPath = join-path $analyzersPath $languageFolder
- if (Test-Path $languageAnalyzersPath)
- {
- foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
- {
- if($project.Object.AnalyzerReferences)
- {
- $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/MessagePack.Analyzers.CodeFixes/tools/uninstall.ps1 b/src/MessagePack.Analyzers.CodeFixes/tools/uninstall.ps1
deleted file mode 100644
index 7d9c8cc1d..000000000
--- a/src/MessagePack.Analyzers.CodeFixes/tools/uninstall.ps1
+++ /dev/null
@@ -1,65 +0,0 @@
-param($installPath, $toolsPath, $package, $project)
-
-if($project.Object.SupportsPackageDependencyResolution)
-{
- if($project.Object.SupportsPackageDependencyResolution())
- {
- # Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it.
- return
- }
-}
-
-$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
-
-foreach($analyzersPath in $analyzersPaths)
-{
- # Uninstall the language agnostic analyzers.
- if (Test-Path $analyzersPath)
- {
- foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
- {
- if($project.Object.AnalyzerReferences)
- {
- $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
- }
- }
- }
-}
-
-# $project.Type gives the language name like (C# or VB.NET)
-$languageFolder = ""
-if($project.Type -eq "C#")
-{
- $languageFolder = "cs"
-}
-if($project.Type -eq "VB.NET")
-{
- $languageFolder = "vb"
-}
-if($languageFolder -eq "")
-{
- return
-}
-
-foreach($analyzersPath in $analyzersPaths)
-{
- # Uninstall language specific analyzers.
- $languageAnalyzersPath = join-path $analyzersPath $languageFolder
- if (Test-Path $languageAnalyzersPath)
- {
- foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
- {
- if($project.Object.AnalyzerReferences)
- {
- try
- {
- $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
- }
- catch
- {
-
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/MessagePack.Analyzers.CodeFixes/version.json b/src/MessagePack.Analyzers.CodeFixes/version.json
new file mode 100644
index 000000000..7142e661f
--- /dev/null
+++ b/src/MessagePack.Analyzers.CodeFixes/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
+ "inherit": true,
+ "assemblyVersion": {
+ "precision": "revision"
+ }
+}
diff --git a/src/MessagePack.Analyzers/.editorconfig b/src/MessagePack.Analyzers/.editorconfig
deleted file mode 100644
index c7bcd1010..000000000
--- a/src/MessagePack.Analyzers/.editorconfig
+++ /dev/null
@@ -1,4 +0,0 @@
-[*.cs]
-
-# CA1062: Validate arguments of public methods
-dotnet_diagnostic.CA1062.severity = silent
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/AnalyzerOptions.cs b/src/MessagePack.Analyzers/CodeAnalysis/AnalyzerOptions.cs
deleted file mode 100644
index b6a0859bb..000000000
--- a/src/MessagePack.Analyzers/CodeAnalysis/AnalyzerOptions.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Immutable;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Diagnostics;
-
-namespace MessagePack.Analyzers.CodeAnalysis;
-
-#pragma warning disable SA1402 // File may only contain a single type
-
-///
-/// Options for the analyzer and source generator, which may be deserialized from a MessagePackAnalyzer.json file.
-///
-public record AnalyzerOptions
-{
- public const string RootNamespace = "build_property.RootNamespace";
- public const string JsonOptionsFileName = "MessagePackAnalyzer.json";
-
- public static readonly AnalyzerOptions Default = new AnalyzerOptions();
-
- ///
- /// Gets an array of fully-qualified names of types that are included in serialized object graphs but are assumed to have custom formatters registered already.
- ///
- public ImmutableHashSet CustomFormattedTypes { get; init; } = ImmutableHashSet.Empty;
-
- public GeneratorOptions Generator { get; init; } = new();
-
- public string FormatterNamespace => this.Generator.Formatters.Namespace;
-
- ///
- /// Gets a value indicating whether the analyzer is generating source code.
- ///
- public bool IsGeneratingSource { get; init; }
-
- public static AnalyzerOptions Parse(AnalyzerConfigOptions options, ImmutableArray additionalTexts, CancellationToken cancellationToken)
- {
- // The default namespace for the resolver comes from the project root namespace.
- AnalyzerOptions result = Default;
-
- if (additionalTexts.FirstOrDefault(x => string.Equals(Path.GetFileName(x.Path), JsonOptionsFileName, StringComparison.OrdinalIgnoreCase))?.GetText(cancellationToken)?.ToString() is string configJson)
- {
- result = JsonSerializer.Deserialize(
- configJson,
- new JsonSerializerOptions
- {
- AllowTrailingCommas = true,
- MaxDepth = 5,
- PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
- ReadCommentHandling = JsonCommentHandling.Skip,
- }) ?? Default;
- }
-
- if (result.Generator.Resolver.Namespace is null)
- {
- if (!options.TryGetValue(RootNamespace, out string? resolverNamespace))
- {
- resolverNamespace = "MessagePack";
- }
-
- result = result with { Generator = result.Generator with { Resolver = result.Generator.Resolver with { Namespace = resolverNamespace } } };
- }
-
- return result;
- }
-}
-
-///
-/// Customizes aspects of source generated formatters.
-///
-public record FormattersOptions
-{
- ///
- /// The default options.
- ///
- public static readonly FormattersOptions Default = new();
-
- ///
- /// Gets the root namespace into which formatters are emitted.
- ///
- public string Namespace { get; init; } = "Formatters";
-}
-
-///
-/// Describes the generated resolver.
-///
-public record ResolverOptions
-{
- ///
- /// The default options.
- ///
- public static readonly ResolverOptions Default = new();
-
- ///
- /// Gets a value indicating whether the generated resolver should be public (as opposed to internal).
- /// A public resolver is appropriate when developing a library that may be used by another assembly that needs to aggregate this generated resolver with others.
- ///
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
- public bool Public { get; init; }
-
- ///
- /// Gets the name to use for the resolver.
- ///
- public string Name { get; init; } = "GeneratedMessagePackResolver";
-
- ///
- /// Gets the namespace the source generated resolver will be emitted into.
- ///
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
- public string? Namespace { get; init; }
-}
-
-///
-/// Customizes AOT source generation of formatters for custom types.
-///
-public record GeneratorOptions
-{
- ///
- /// The default options.
- ///
- public static readonly GeneratorOptions Default = new();
-
- ///
- /// Gets a value indicating whether types will be serialized with their property names as well as their values in a key=value dictionary, as opposed to an array of values.
- ///
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
- public bool UsesMapMode { get; init; }
-
- ///
- /// Gets options for the generated resolver.
- ///
- public ResolverOptions Resolver { get; init; } = new();
-
- ///
- /// Gets options for the generated formatter.
- ///
- public FormattersOptions Formatters { get; init; } = new();
-}
diff --git a/src/MessagePack.Analyzers/Directory.Build.props b/src/MessagePack.Analyzers/Directory.Build.props
deleted file mode 100644
index ea193bb7a..000000000
--- a/src/MessagePack.Analyzers/Directory.Build.props
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- true
-
-
-
diff --git a/src/MessagePack.Analyzers/MessagePack.Analyzers.csproj b/src/MessagePack.Analyzers/MessagePack.Analyzers.csproj
index 2eeb2e55f..87d64415d 100644
--- a/src/MessagePack.Analyzers/MessagePack.Analyzers.csproj
+++ b/src/MessagePack.Analyzers/MessagePack.Analyzers.csproj
@@ -1,38 +1,47 @@
+
netstandard2.0
- enable
- false
- MessagePack.Analyzers.Only
+ MessagePackAnalyzer
- $(CodeAnalysisVersionForUnity)
+ Analyzers and source generator for MessagePack for C#. Verify rules for [MessagePackObject] and code fix for [Key]. A roslyn source generator for AOT or faster startup of applications that use the MessagePack nuget package.
+ MessagePack Source Generator and Analyzer
+ MsgPack;MessagePack;Serialization;Formatter;Analyzer
+ false
+ $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs
+ true
+ false
+ true
+
-
- true
- build\
-
-
-
-
-
-
-
-
-
-
-
-
-
- True
- True
- Strings.resx
-
+
+
+
+
+
+ all
+
+
+
-
- ResXFileCodeGenerator
- Strings.Designer.cs
-
+
+
+
+
+
+
+
+ $(CodeAnalysisVersionForUnity.Substring(0, $(CodeAnalysisVersionForUnity.LastIndexOf('.'))))
+ $(MicrosoftCodeAnalysisVersion.Substring(0, $(MicrosoftCodeAnalysisVersion.LastIndexOf('.'))))
+
+
+
+
+
+
diff --git a/src/MessagePack.Analyzers/Strings.Designer.cs b/src/MessagePack.Analyzers/Strings.Designer.cs
deleted file mode 100644
index 8a0503562..000000000
--- a/src/MessagePack.Analyzers/Strings.Designer.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace MessagePack.Analyzers {
- using System;
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Strings {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Strings() {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MessagePack.Analyzers.Strings", typeof(Strings).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- ///
- /// Looks up a localized string similar to Without an explicit MessagePackSerializerOptions argument, a shared/static default will be assumed which any other code in the entire AppDomain or process can alter, causing malfunction in your invocation..
- ///
- internal static string MsgPack001_Description {
- get {
- return ResourceManager.GetString("MsgPack001_Description", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Supply a value for the MessagePackSerializerOptions argument.
- ///
- internal static string MsgPack001_MessageFormat {
- get {
- return ResourceManager.GetString("MsgPack001_MessageFormat", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Avoid mutable default options.
- ///
- internal static string MsgPack001_Title {
- get {
- return ResourceManager.GetString("MsgPack001_Title", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Providing a static value for MessagePackSerializerOptions from a mutable member can lead to malfunction when that member is mutated by another party..
- ///
- internal static string MsgPack002_Description {
- get {
- return ResourceManager.GetString("MsgPack002_Description", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Avoid using a mutable static value for MessagePackSerializerOptions.
- ///
- internal static string MsgPack002_MessageFormat {
- get {
- return ResourceManager.GetString("MsgPack002_MessageFormat", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Avoid mutable static options.
- ///
- internal static string MsgPack002_Title {
- get {
- return ResourceManager.GetString("MsgPack002_Title", resourceCulture);
- }
- }
- }
-}
diff --git a/src/MessagePack.Analyzers/Transforms/ShouldUseFormatterResolverHelper.cs b/src/MessagePack.Analyzers/Transforms/ShouldUseFormatterResolverHelper.cs
deleted file mode 100644
index 892f46222..000000000
--- a/src/MessagePack.Analyzers/Transforms/ShouldUseFormatterResolverHelper.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-namespace MessagePack.Analyzers.Transforms;
-
-public static class ShouldUseFormatterResolverHelper
-{
- ///
- /// Keep this list in sync with DynamicObjectTypeBuilder.IsOptimizeTargetType.
- ///
- internal static readonly string[] PrimitiveTypes =
- {
- "short",
- "int",
- "long",
- "ushort",
- "uint",
- "ulong",
- "float",
- "double",
- "bool",
- "byte",
- "sbyte",
- "char",
- "byte[]",
-
- // Do not include types that resolvers are allowed to modify.
- ////"global::System.DateTime", // OldSpec has no support, so for that and perf reasons a .NET native DateTime resolver exists.
- ////"string", // https://github.com/Cysharp/MasterMemory provides custom formatter for string interning.
- };
-
- public static bool ShouldUseFormatterResolver(MemberSerializationInfo[] infos)
- {
- foreach (var memberSerializationInfo in infos)
- {
- if (memberSerializationInfo.CustomFormatterTypeName == null && Array.IndexOf(PrimitiveTypes, memberSerializationInfo.Type) == -1)
- {
- return true;
- }
- }
-
- return false;
- }
-}
diff --git a/src/MessagePack.Analyzers/Utils/AnalyzerUtilities.cs b/src/MessagePack.Analyzers/Utils/AnalyzerUtilities.cs
deleted file mode 100644
index 1094f05c5..000000000
--- a/src/MessagePack.Analyzers/Utils/AnalyzerUtilities.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-namespace MessagePack.Analyzers;
-
-internal static class AnalyzerUtilities
-{
- internal static string GetHelpLink(string diagnosticId) => $"https://github.com/neuecc/MessagePack-CSharp/blob/master/doc/analyzers/{diagnosticId}.md";
-}
diff --git a/src/MessagePack.Analyzers/build/MessagePack.Analyzers.targets b/src/MessagePack.Analyzers/build/MessagePack.Analyzers.targets
deleted file mode 100644
index 6ecb44bab..000000000
--- a/src/MessagePack.Analyzers/build/MessagePack.Analyzers.targets
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/src/MessagePack.Analyzers/build/MessagePackAnalyzer.targets b/src/MessagePack.Analyzers/build/MessagePackAnalyzer.targets
new file mode 100644
index 000000000..05afdf84e
--- /dev/null
+++ b/src/MessagePack.Analyzers/build/MessagePackAnalyzer.targets
@@ -0,0 +1,9 @@
+
+
+
+ <_ObsoleteAnalyzerJson Include="@(AdditionalFiles)" Condition="'%(FileName)%(Extension)' == 'MessagePackAnalyzer.json'" />
+
+
+
+
diff --git a/src/MessagePack.SourceGenerator.Unity/MessagePack.SourceGenerator.Unity.csproj b/src/MessagePack.SourceGenerator.Unity/MessagePack.SourceGenerator.Unity.csproj
index 9748aea66..48c2f11d6 100644
--- a/src/MessagePack.SourceGenerator.Unity/MessagePack.SourceGenerator.Unity.csproj
+++ b/src/MessagePack.SourceGenerator.Unity/MessagePack.SourceGenerator.Unity.csproj
@@ -5,34 +5,34 @@
$(DefineConstants);UNITY
false
+ MessagePack.SourceGenerator
$(CodeAnalysisVersionForUnity)
-
+
+
+
+
+
+
+
+
+
+
-
-
- $(IntermediateOutputPath)zip\
-
-
-
-
-
- $(ZipStagingDirectory)%(FileName)%(Extension)
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/MessagePack.SourceGenerator.Unity/MessagePackGenerator.cs b/src/MessagePack.SourceGenerator.Unity/MessagePackGenerator.cs
index 26a9c6711..d45b0f7ad 100644
--- a/src/MessagePack.SourceGenerator.Unity/MessagePackGenerator.cs
+++ b/src/MessagePack.SourceGenerator.Unity/MessagePackGenerator.cs
@@ -2,8 +2,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Immutable;
-using MessagePack.Analyzers.CodeAnalysis;
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace MessagePack.SourceGenerator;
@@ -11,8 +11,6 @@ namespace MessagePack.SourceGenerator;
[Generator]
public partial class MessagePackGenerator : ISourceGenerator
{
- public const string MessagePackObjectAttributeFullName = "MessagePack.MessagePackObjectAttribute";
-
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(SyntaxContextReceiver.Create);
@@ -20,32 +18,55 @@ public void Initialize(GeneratorInitializationContext context)
public void Execute(GeneratorExecutionContext context)
{
- if (context.SyntaxReceiver is not SyntaxContextReceiver receiver || receiver.ClassDeclarations.Count == 0)
+ if (context.SyntaxReceiver is not SyntaxContextReceiver receiver || receiver.TypeDeclarations.Count == 0)
{
return;
}
- Compilation compilation = context.Compilation;
+ CSharpCompilation compilation = (CSharpCompilation)context.Compilation;
if (!ReferenceSymbols.TryCreate(compilation, out ReferenceSymbols? referenceSymbols))
{
return;
}
- AnalyzerOptions options = AnalyzerOptions.Parse(context.AnalyzerConfigOptions.GlobalOptions, context.AdditionalFiles, context.CancellationToken) with { IsGeneratingSource = true };
+ // Search for a resolver generator attribute, which may be applied to any type in the compilation.
+ AnalyzerOptions? options = new();
+ foreach (var typeDeclByDocument in receiver.TypeDeclarations.GroupBy(td => td.SyntaxTree))
+ {
+ SemanticModel semanticModel = compilation.GetSemanticModel(typeDeclByDocument.Key, ignoreAccessibility: true);
+ foreach (TypeDeclarationSyntax typeDecl in typeDeclByDocument)
+ {
+ if (semanticModel.GetDeclaredSymbol(typeDecl, context.CancellationToken) is INamedTypeSymbol typeSymbol)
+ {
+ if (AnalyzerUtilities.ParseGeneratorAttribute(typeSymbol.GetAttributes(), typeSymbol, context.CancellationToken) is AnalyzerOptions resolverOptions)
+ {
+ options = resolverOptions;
+ break;
+ }
+ }
+ }
+ }
+
+ // Collect and apply the assembly-level attributes to the options.
+ options = options.WithAssemblyAttributes(compilation.Assembly.GetAttributes(), context.CancellationToken);
List modelPerType = new();
- foreach (var syntax in receiver.ClassDeclarations)
+ foreach (var syntax in receiver.TypeDeclarations)
{
- if (TypeCollector.Collect(compilation, options, referenceSymbols, null, syntax, context.CancellationToken) is FullModel model)
+ if (TypeCollector.Collect(compilation, options, referenceSymbols, reportAnalyzerDiagnostic: null, syntax, context.CancellationToken) is FullModel model)
{
modelPerType.Add(model);
}
}
FullModel fullModel = FullModel.Combine(modelPerType.ToImmutableArray());
- GeneratorContext generateContext = new(context);
- Generate(generateContext, fullModel);
- GenerateResolver(generateContext, fullModel);
+
+ if (options.IsGeneratingSource)
+ {
+ GeneratorContext generateContext = new(context);
+ Generate(generateContext, fullModel);
+ GenerateResolver(generateContext, fullModel);
+ }
}
private class SyntaxContextReceiver : ISyntaxReceiver
@@ -55,29 +76,21 @@ internal static ISyntaxReceiver Create()
return new SyntaxContextReceiver();
}
- public HashSet ClassDeclarations { get; } = new();
+ public HashSet TypeDeclarations { get; } = new();
+
+ public HashSet AssemblyLevelAttributes { get; } = new();
public void OnVisitSyntaxNode(SyntaxNode context)
{
- if (context is TypeDeclarationSyntax typeSyntax)
+ switch (context)
{
- if (typeSyntax.AttributeLists.Count > 0)
- {
- var hasAttribute = typeSyntax.AttributeLists
- .SelectMany(x => x.Attributes)
- .Any(x => x.Name.ToString() is "MessagePackObject"
- or "MessagePackObjectAttribute"
- or "MessagePack.MessagePackObject"
- or "MessagePack.MessagePackObjectAttribute"
- or "Union"
- or "UnionAttribute"
- or "MessagePack.Union"
- or "MessagePack.UnionAttribute");
- if (hasAttribute)
- {
- ClassDeclarations.Add(typeSyntax);
- }
- }
+ // Any type with attributes warrants a review when we have the semantic model available.
+ case TypeDeclarationSyntax { AttributeLists.Count: > 0 } typeSyntax:
+ this.TypeDeclarations.Add(typeSyntax);
+ break;
+ case AttributeSyntax { Parent: AttributeListSyntax { Parent: CompilationUnitSyntax } } attributeSyntax:
+ this.AssemblyLevelAttributes.Add(attributeSyntax);
+ break;
}
}
}
diff --git a/src/MessagePack.Analyzers/AnalyzerReleases.Shipped.md b/src/MessagePack.SourceGenerator/AnalyzerReleases.Shipped.md
similarity index 100%
rename from src/MessagePack.Analyzers/AnalyzerReleases.Shipped.md
rename to src/MessagePack.SourceGenerator/AnalyzerReleases.Shipped.md
diff --git a/src/MessagePack.Analyzers/AnalyzerReleases.Unshipped.md b/src/MessagePack.SourceGenerator/AnalyzerReleases.Unshipped.md
similarity index 100%
rename from src/MessagePack.Analyzers/AnalyzerReleases.Unshipped.md
rename to src/MessagePack.SourceGenerator/AnalyzerReleases.Unshipped.md
diff --git a/src/MessagePack.Analyzers/MsgPack001SpecifyOptionsAnalyzer.cs b/src/MessagePack.SourceGenerator/Analyzers/MsgPack001SpecifyOptionsAnalyzer.cs
similarity index 96%
rename from src/MessagePack.Analyzers/MsgPack001SpecifyOptionsAnalyzer.cs
rename to src/MessagePack.SourceGenerator/Analyzers/MsgPack001SpecifyOptionsAnalyzer.cs
index ff294d865..57cc9fbea 100644
--- a/src/MessagePack.Analyzers/MsgPack001SpecifyOptionsAnalyzer.cs
+++ b/src/MessagePack.SourceGenerator/Analyzers/MsgPack001SpecifyOptionsAnalyzer.cs
@@ -2,17 +2,16 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Immutable;
-using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
-namespace MessagePack.Analyzers;
+namespace MessagePack.SourceGenerator.Analyzers;
///
/// An analyzer that guards against calling APIs that rely on static, mutable fields defining "default" options.
///
-[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MsgPack001SpecifyOptionsAnalyzer : DiagnosticAnalyzer
{
public const string MissingOptionsId = "MsgPack001";
diff --git a/src/MessagePack.Analyzers/MsgPack002UseConstantOptionsAnalyzer.cs b/src/MessagePack.SourceGenerator/Analyzers/MsgPack002UseConstantOptionsAnalyzer.cs
similarity index 97%
rename from src/MessagePack.Analyzers/MsgPack002UseConstantOptionsAnalyzer.cs
rename to src/MessagePack.SourceGenerator/Analyzers/MsgPack002UseConstantOptionsAnalyzer.cs
index b26e3dc64..835752d71 100644
--- a/src/MessagePack.Analyzers/MsgPack002UseConstantOptionsAnalyzer.cs
+++ b/src/MessagePack.SourceGenerator/Analyzers/MsgPack002UseConstantOptionsAnalyzer.cs
@@ -6,12 +6,12 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
-namespace MessagePack.Analyzers;
+namespace MessagePack.SourceGenerator.Analyzers;
///
/// An analyzer to guide callers to avoid use of mutable static fields for MessagePackSerializerOptions.
///
-[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MsgPack002UseConstantOptionsAnalyzer : DiagnosticAnalyzer
{
public const string MutableSharedOptionsId = "MsgPack002";
diff --git a/src/MessagePack.Analyzers/MsgPack00xMessagePackAnalyzer.cs b/src/MessagePack.SourceGenerator/Analyzers/MsgPack00xMessagePackAnalyzer.cs
similarity index 85%
rename from src/MessagePack.Analyzers/MsgPack00xMessagePackAnalyzer.cs
rename to src/MessagePack.SourceGenerator/Analyzers/MsgPack00xMessagePackAnalyzer.cs
index e206387b4..d673fc654 100644
--- a/src/MessagePack.Analyzers/MsgPack00xMessagePackAnalyzer.cs
+++ b/src/MessagePack.SourceGenerator/Analyzers/MsgPack00xMessagePackAnalyzer.cs
@@ -3,11 +3,10 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
+using AnalyzerOptions = MessagePack.SourceGenerator.CodeAnalysis.AnalyzerOptions;
-namespace MessagePack.Analyzers;
+namespace MessagePack.SourceGenerator.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer
@@ -21,11 +20,10 @@ public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer
internal const string Category = "Usage";
- internal const string MessagePackObjectAttributeShortName = "MessagePackObjectAttribute";
+ internal const string MessagePackObjectAttributeShortName = Constants.MessagePackObjectAttributeName;
internal const string KeyAttributeShortName = "KeyAttribute";
internal const string IgnoreShortName = "IgnoreMemberAttribute";
internal const string IgnoreDataMemberShortName = "IgnoreDataMemberAttribute";
- internal const string UnionAttributeShortName = "UnionAttribute";
internal static readonly DiagnosticDescriptor TypeMustBeMessagePackObject = new DiagnosticDescriptor(
id: UseMessagePackObjectAttributeId,
@@ -197,31 +195,25 @@ public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
- context.RegisterCompilationStartAction(ctxt =>
+ context.RegisterCompilationStartAction(context =>
{
- CodeAnalysis.AnalyzerOptions options = CodeAnalysis.AnalyzerOptions.Parse(ctxt.Options.AnalyzerConfigOptionsProvider.GlobalOptions, ctxt.Options.AdditionalFiles, ctxt.CancellationToken);
- if (ReferenceSymbols.TryCreate(ctxt.Compilation, out ReferenceSymbols? typeReferences))
+ if (ReferenceSymbols.TryCreate(context.Compilation, out ReferenceSymbols? typeReferences))
{
- ctxt.RegisterSyntaxNodeAction(c => Analyze(c, typeReferences, options), SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.InterfaceDeclaration, SyntaxKind.RecordDeclaration);
+ AnalyzerOptions options = new AnalyzerOptions().WithAssemblyAttributes(context.Compilation.Assembly.GetAttributes(), context.CancellationToken);
+ context.RegisterSymbolAction(context => this.AnalyzeSymbol(context, typeReferences, options), SymbolKind.NamedType);
}
});
}
- private static void Analyze(SyntaxNodeAnalysisContext context, ReferenceSymbols typeReferences, CodeAnalysis.AnalyzerOptions options)
+ private void AnalyzeSymbol(SymbolAnalysisContext context, ReferenceSymbols typeReferences, AnalyzerOptions options)
{
- TypeDeclarationSyntax typeDeclaration = (TypeDeclarationSyntax)context.Node;
- INamedTypeSymbol? declaredSymbol = context.SemanticModel.GetDeclaredSymbol(typeDeclaration);
- if (declaredSymbol is null)
+ INamedTypeSymbol declaredSymbol = (INamedTypeSymbol)context.Symbol;
+ switch (declaredSymbol.TypeKind)
{
- return;
- }
-
- if (
- ((declaredSymbol.TypeKind == TypeKind.Interface) && declaredSymbol.GetAttributes().Any(x2 => SymbolEqualityComparer.Default.Equals(x2.AttributeClass, typeReferences.UnionAttribute)))
- || ((declaredSymbol.TypeKind == TypeKind.Class) && declaredSymbol.GetAttributes().Any(x2 => SymbolEqualityComparer.Default.Equals(x2.AttributeClass, typeReferences.MessagePackObjectAttribute)))
- || ((declaredSymbol.TypeKind == TypeKind.Struct) && declaredSymbol.GetAttributes().Any(x2 => SymbolEqualityComparer.Default.Equals(x2.AttributeClass, typeReferences.MessagePackObjectAttribute))))
- {
- TypeCollector.Collect(context.Compilation, options, typeReferences, context.ReportDiagnostic, declaredSymbol);
+ 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);
+ break;
}
}
}
diff --git a/src/MessagePack.SourceGenerator/CodeAnalysis/AnalyzerOptions.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/AnalyzerOptions.cs
new file mode 100644
index 000000000..ea3792a3e
--- /dev/null
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/AnalyzerOptions.cs
@@ -0,0 +1,101 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#pragma warning disable SA1402 // File may only contain a single type
+
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+
+namespace MessagePack.SourceGenerator.CodeAnalysis;
+
+///
+/// Options for the analyzer and source generator.
+///
+///
+/// These options are typically gathered from attributes in the compilation.
+///
+public record AnalyzerOptions
+{
+ ///
+ /// Gets the set fully qualified names of types that are assumed to have custom formatters written that will be included by a resolver by the program.
+ ///
+ public ImmutableHashSet AssumedFormattableTypes { get; init; } = ImmutableHashSet.Empty;
+
+ ///
+ /// Gets the set fully qualified names of custom formatters that should be considered by the analyzer and included in the generated resolver,
+ /// and the collection of types that they can format.
+ ///
+ public ImmutableDictionary> KnownFormatters { get; init; } = ImmutableDictionary>.Empty;
+
+ public GeneratorOptions Generator { get; init; } = new();
+
+ ///
+ /// Gets a value indicating whether the analyzer is generating source code.
+ ///
+ public bool IsGeneratingSource { get; init; }
+
+ internal AnalyzerOptions WithFormatterTypes(ImmutableArray formattableTypes, ImmutableDictionary> formatterTypes)
+ {
+ return this with
+ {
+ AssumedFormattableTypes = ImmutableHashSet.CreateRange(formattableTypes).Union(formatterTypes.SelectMany(t => t.Value)),
+ KnownFormatters = formatterTypes,
+ };
+ }
+
+ ///
+ /// Modifies these options based on the attributes on the assembly being compiled.
+ ///
+ /// The assembly-level attributes.
+ /// A cancellation token.
+ /// The modified set of options.
+ internal AnalyzerOptions WithAssemblyAttributes(ImmutableArray assemblyAttributes, CancellationToken cancellationToken)
+ {
+ ImmutableDictionary> customFormatters = AnalyzerUtilities.ParseKnownFormatterAttribute(assemblyAttributes, cancellationToken);
+ ImmutableArray customFormattedTypes = AnalyzerUtilities.ParseAssumedFormattableAttribute(assemblyAttributes, cancellationToken);
+ return this.WithFormatterTypes(customFormattedTypes, customFormatters);
+ }
+}
+
+///
+/// Customizes aspects of source generated formatters.
+///
+public record FormattersOptions
+{
+ ///
+ /// Gets a value indicating whether types will be serialized with their property names as well as their values in a key=value dictionary, as opposed to an array of values.
+ ///
+ public bool UsesMapMode { get; init; }
+}
+
+///
+/// Describes the generated resolver.
+///
+public record ResolverOptions
+{
+ ///
+ /// Gets the name to use for the resolver.
+ ///
+ public string Name { get; init; } = "GeneratedMessagePackResolver";
+
+ ///
+ /// Gets the namespace the source generated resolver will be emitted into.
+ ///
+ public string? Namespace { get; init; } = "MessagePack";
+}
+
+///
+/// Customizes AOT source generation of formatters for custom types.
+///
+public record GeneratorOptions
+{
+ ///
+ /// Gets options for the generated resolver.
+ ///
+ public ResolverOptions Resolver { get; init; } = new();
+
+ ///
+ /// Gets options for the generated formatter.
+ ///
+ public FormattersOptions Formatters { get; init; } = new();
+}
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/CodeAnalysisUtilities.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/CodeAnalysisUtilities.cs
similarity index 96%
rename from src/MessagePack.Analyzers/CodeAnalysis/CodeAnalysisUtilities.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/CodeAnalysisUtilities.cs
index 21794abea..af52ccddc 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/CodeAnalysisUtilities.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/CodeAnalysisUtilities.cs
@@ -1,7 +1,7 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public static class CodeAnalysisUtilities
{
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/EnumSerializationInfo.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/EnumSerializationInfo.cs
similarity index 67%
rename from src/MessagePack.Analyzers/CodeAnalysis/EnumSerializationInfo.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/EnumSerializationInfo.cs
index 564054539..357c62130 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/EnumSerializationInfo.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/EnumSerializationInfo.cs
@@ -3,17 +3,13 @@
using Microsoft.CodeAnalysis;
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public sealed record EnumSerializationInfo(string? Namespace, string Name, string FullName, string UnderlyingTypeName) : IResolverRegisterInfo
{
- public IReadOnlyCollection Diagnostics { get; init; } = Array.Empty();
+ public string FileNameHint => $"{CodeAnalysisUtilities.AppendNameToNamespace("Formatters", this.Namespace)}.{this.FormatterName}";
- public string FileNameHint => $"{CodeAnalysisUtilities.AppendNameToNamespace("Formatters", this.Namespace)}.{this.FormatterNameWithoutNamespace}";
-
- public string FormatterName => CodeAnalysisUtilities.QualifyWithOptionalNamespace(this.FormatterNameWithoutNamespace, $"Formatters::{this.Namespace}");
-
- public string FormatterNameWithoutNamespace => this.Name + "Formatter";
+ public string FormatterName => this.Name + "Formatter";
public string UnderlyingTypeKeyword => this.UnderlyingTypeName switch
{
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/FullModel.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/FullModel.cs
similarity index 94%
rename from src/MessagePack.Analyzers/CodeAnalysis/FullModel.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/FullModel.cs
index f04b56685..be866cd40 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/FullModel.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/FullModel.cs
@@ -2,8 +2,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public record FullModel(
ImmutableSortedSet ObjectInfos,
@@ -29,7 +30,7 @@ public static FullModel Combine(ImmutableArray models)
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
- AnalyzerOptions.Default);
+ new AnalyzerOptions());
}
AnalyzerOptions options = models[0].Options;
@@ -37,6 +38,7 @@ public static FullModel Combine(ImmutableArray models)
var enumInfos = ImmutableSortedSet.CreateBuilder(ResolverRegisterInfoComparer.Default);
var genericInfos = ImmutableSortedSet.CreateBuilder(ResolverRegisterInfoComparer.Default);
var unionInfos = ImmutableSortedSet.CreateBuilder(ResolverRegisterInfoComparer.Default);
+ var diagnostics = ImmutableArray.CreateBuilder();
foreach (FullModel model in models)
{
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/GenericSerializationInfo.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/GenericSerializationInfo.cs
similarity index 69%
rename from src/MessagePack.Analyzers/CodeAnalysis/GenericSerializationInfo.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/GenericSerializationInfo.cs
index b1f91bc47..f4184e2b2 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/GenericSerializationInfo.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/GenericSerializationInfo.cs
@@ -3,12 +3,10 @@
using Microsoft.CodeAnalysis;
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
-public sealed record GenericSerializationInfo(string FullName, string FormatterName, bool IsOpenGenericType) : IResolverRegisterInfo
+public sealed record GenericSerializationInfo(string FullName, string FormatterName, string? Namespace, bool IsOpenGenericType) : IResolverRegisterInfo
{
- public IReadOnlyCollection Diagnostics { get; init; } = Array.Empty();
-
public bool Equals(GenericSerializationInfo? other)
{
return this.FullName.Equals(other?.FullName);
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/GenericTypeParameterInfo.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/GenericTypeParameterInfo.cs
similarity index 85%
rename from src/MessagePack.Analyzers/CodeAnalysis/GenericTypeParameterInfo.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/GenericTypeParameterInfo.cs
index 6784eb21a..9268ff41c 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/GenericTypeParameterInfo.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/GenericTypeParameterInfo.cs
@@ -1,7 +1,7 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public record GenericTypeParameterInfo(string Name, string Constraints)
{
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/IResolverRegisterInfo.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/IResolverRegisterInfo.cs
similarity index 74%
rename from src/MessagePack.Analyzers/CodeAnalysis/IResolverRegisterInfo.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/IResolverRegisterInfo.cs
index 5a88ec2c7..2b37ab46a 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/IResolverRegisterInfo.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/IResolverRegisterInfo.cs
@@ -3,7 +3,7 @@
using Microsoft.CodeAnalysis;
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public interface IResolverRegisterInfo
{
@@ -11,5 +11,5 @@ public interface IResolverRegisterInfo
string FormatterName { get; }
- IReadOnlyCollection Diagnostics { get; }
+ string? Namespace { get; }
}
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/MemberSerializationInfo.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/MemberSerializationInfo.cs
similarity index 91%
rename from src/MessagePack.Analyzers/CodeAnalysis/MemberSerializationInfo.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/MemberSerializationInfo.cs
index 4fa9045c9..41dd94e3e 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/MemberSerializationInfo.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/MemberSerializationInfo.cs
@@ -1,9 +1,7 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using MessagePack.Analyzers.Transforms;
-
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public record MemberSerializationInfo(
bool IsProperty,
@@ -16,7 +14,7 @@ public record MemberSerializationInfo(
string ShortTypeName,
string? CustomFormatterTypeName)
{
- private static readonly IReadOnlyCollection PrimitiveTypes = new HashSet(ShouldUseFormatterResolverHelper.PrimitiveTypes);
+ private static readonly IReadOnlyCollection PrimitiveTypes = new HashSet(AnalyzerUtilities.PrimitiveTypes);
public string GetSerializeMethodString()
{
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/ObjectSerializationInfo.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/ObjectSerializationInfo.cs
similarity index 84%
rename from src/MessagePack.Analyzers/CodeAnalysis/ObjectSerializationInfo.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/ObjectSerializationInfo.cs
index 4eb40228c..e2cb672f4 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/ObjectSerializationInfo.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/ObjectSerializationInfo.cs
@@ -3,7 +3,7 @@
using Microsoft.CodeAnalysis;
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public record ObjectSerializationInfo(
bool IsClass,
@@ -24,11 +24,9 @@ public bool IsStringKey
get { return !this.IsIntKey; }
}
- public string FileNameHint => $"{CodeAnalysisUtilities.AppendNameToNamespace("Formatters", this.Namespace)}.{this.FormatterNameWithoutNamespace}";
+ public string FileNameHint => $"{CodeAnalysisUtilities.AppendNameToNamespace("Formatters", this.Namespace)}.{this.FormatterName}";
- public string FormatterName => CodeAnalysisUtilities.QualifyWithOptionalNamespace(this.FormatterNameWithoutNamespace, $"Formatters::{this.Namespace}");
-
- public string FormatterNameWithoutNamespace => this.Name + "Formatter" + (this.IsOpenGenericType ? $"<{string.Join(", ", this.GenericTypeParameters.Select(x => x.Name))}>" : string.Empty);
+ public string FormatterName => this.Name + "Formatter" + (this.IsOpenGenericType ? $"<{string.Join(", ", this.GenericTypeParameters.Select(x => x.Name))}>" : string.Empty);
public int WriteCount
{
@@ -53,8 +51,6 @@ public int MaxKey
}
}
- public IReadOnlyCollection Diagnostics { get; init; } = Array.Empty();
-
public MemberSerializationInfo? GetMember(int index)
{
return this.Members.FirstOrDefault(x => x.IntKey == index);
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/ReferenceSymbols.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/ReferenceSymbols.cs
similarity index 98%
rename from src/MessagePack.Analyzers/CodeAnalysis/ReferenceSymbols.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/ReferenceSymbols.cs
index dd7ea1ab1..b10b12512 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/ReferenceSymbols.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/ReferenceSymbols.cs
@@ -4,7 +4,7 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public record ReferenceSymbols(
INamedTypeSymbol MessagePackObjectAttribute,
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/ResolverRegisterInfoComparer.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/ResolverRegisterInfoComparer.cs
similarity index 90%
rename from src/MessagePack.Analyzers/CodeAnalysis/ResolverRegisterInfoComparer.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/ResolverRegisterInfoComparer.cs
index 12c43b363..9d6247f24 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/ResolverRegisterInfoComparer.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/ResolverRegisterInfoComparer.cs
@@ -1,7 +1,7 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public class ResolverRegisterInfoComparer : IComparer
{
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/TypeCollector.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/TypeCollector.cs
similarity index 95%
rename from src/MessagePack.Analyzers/CodeAnalysis/TypeCollector.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/TypeCollector.cs
index 3e95d93ce..4205020a3 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/TypeCollector.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/TypeCollector.cs
@@ -11,7 +11,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Operations;
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public class MessagePackGeneratorResolveFailedException : Exception
{
@@ -21,11 +21,6 @@ public MessagePackGeneratorResolveFailedException(string message)
}
}
-internal static class AnalyzerUtilities
-{
- internal static string GetHelpLink(string diagnosticId) => $"https://github.com/neuecc/MessagePack-CSharp/blob/master/doc/analyzers/{diagnosticId}.md";
-}
-
public class TypeCollector
{
private static readonly SymbolDisplayFormat BinaryWriteFormat = new SymbolDisplayFormat(
@@ -185,7 +180,13 @@ public class TypeCollector
private readonly AnalyzerOptions options;
private readonly ReferenceSymbols typeReferences;
+
+ ///
+ /// The means of reporting diagnostics to the analyzer.
+ /// This will be when running in the context of a source generator so as to avoid duplicate diagnostics.
+ ///
private readonly Action? reportDiagnostic;
+
private readonly ITypeSymbol? targetType;
private readonly bool excludeArrayElement;
@@ -200,10 +201,10 @@ public class TypeCollector
private readonly Compilation compilation;
- private TypeCollector(Compilation compilation, AnalyzerOptions options, ReferenceSymbols referenceSymbols, ITypeSymbol targetType, Action? reportDiagnostic)
+ private TypeCollector(Compilation compilation, AnalyzerOptions options, ReferenceSymbols referenceSymbols, ITypeSymbol targetType, Action? reportAnalyzerDiagnostic)
{
this.typeReferences = referenceSymbols;
- this.reportDiagnostic = reportDiagnostic;
+ this.reportDiagnostic = reportAnalyzerDiagnostic;
this.options = options;
this.compilation = compilation;
this.excludeArrayElement = true;
@@ -220,12 +221,12 @@ private TypeCollector(Compilation compilation, AnalyzerOptions options, Referenc
}
}
- public static FullModel? Collect(Compilation compilation, AnalyzerOptions options, ReferenceSymbols referenceSymbols, Action? reportDiagnostic, TypeDeclarationSyntax typeDeclaration, CancellationToken cancellationToken)
+ public static FullModel? Collect(Compilation compilation, AnalyzerOptions options, ReferenceSymbols referenceSymbols, Action? reportAnalyzerDiagnostic, TypeDeclarationSyntax typeDeclaration, CancellationToken cancellationToken)
{
SemanticModel semanticModel = compilation.GetSemanticModel(typeDeclaration.SyntaxTree);
if (semanticModel.GetDeclaredSymbol(typeDeclaration, cancellationToken) is ITypeSymbol typeSymbol)
{
- if (Collect(compilation, options, referenceSymbols, reportDiagnostic, typeSymbol) is FullModel model)
+ if (Collect(compilation, options, referenceSymbols, reportAnalyzerDiagnostic, typeSymbol) is FullModel model)
{
return model;
}
@@ -234,9 +235,9 @@ private TypeCollector(Compilation compilation, AnalyzerOptions options, Referenc
return null;
}
- public static FullModel? Collect(Compilation compilation, AnalyzerOptions options, ReferenceSymbols referenceSymbols, Action? reportDiagnostic, ITypeSymbol targetType)
+ public static FullModel? Collect(Compilation compilation, AnalyzerOptions options, ReferenceSymbols referenceSymbols, Action? reportAnalyzerDiagnostic, ITypeSymbol targetType)
{
- TypeCollector collector = new(compilation, options, referenceSymbols, targetType, reportDiagnostic);
+ TypeCollector collector = new(compilation, options, referenceSymbols, targetType, reportAnalyzerDiagnostic);
if (collector.targetType is null)
{
return null;
@@ -281,7 +282,7 @@ private bool CollectCore(ITypeSymbol typeSymbol)
return result;
}
- var typeSymbolString = typeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated).ToString() ?? throw new InvalidOperationException();
+ var typeSymbolString = typeSymbol.GetCanonicalTypeFullName();
if (EmbeddedTypes.Contains(typeSymbolString))
{
result = true;
@@ -289,7 +290,7 @@ private bool CollectCore(ITypeSymbol typeSymbol)
return result;
}
- if (this.options.CustomFormattedTypes.Contains(typeSymbolString) is true)
+ if (this.options.AssumedFormattableTypes.Contains(typeSymbolString) is true)
{
result = true;
this.alreadyCollected.Add(typeSymbol, result);
@@ -468,7 +469,7 @@ private bool CollectArray(IArrayTypeSymbol array)
}
}
- var info = new GenericSerializationInfo(fullName, formatterName, elemType is ITypeParameterSymbol);
+ var info = new GenericSerializationInfo(fullName, formatterName, null, elemType is ITypeParameterSymbol);
this.collectedGenericInfo.Add(info);
return true;
}
@@ -499,6 +500,7 @@ private bool CollectGeneric(INamedTypeSymbol type)
{
INamedTypeSymbol genericType = type.ConstructUnboundGenericType();
var genericTypeString = genericType.ToDisplayString();
+ string? genericTypeNamespace = genericType.ContainingNamespace?.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
var fullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var isOpenGenericType = this.IsOpenGenericTypeRecursively(type);
@@ -522,7 +524,11 @@ private bool CollectGeneric(INamedTypeSymbol type)
return true;
}
- var info = new GenericSerializationInfo(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), "MsgPack::Formatters.NullableFormatter<" + firstTypeArgument.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + ">", isOpenGenericType);
+ var info = new GenericSerializationInfo(
+ type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
+ "MsgPack::Formatters.NullableFormatter<" + firstTypeArgument.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + ">",
+ Namespace: null,
+ isOpenGenericType);
this.collectedGenericInfo.Add(info);
return true;
}
@@ -538,7 +544,11 @@ private bool CollectGeneric(INamedTypeSymbol type)
var typeArgs = string.Join(", ", type.TypeArguments.Select(x => x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)));
var f = formatter.Replace("TREPLACE", typeArgs);
- var info = new GenericSerializationInfo(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), f, isOpenGenericType);
+ var info = new GenericSerializationInfo(
+ type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
+ f,
+ null,
+ isOpenGenericType);
this.collectedGenericInfo.Add(info);
@@ -550,14 +560,22 @@ private bool CollectGeneric(INamedTypeSymbol type)
formatter = KnownGenericTypes["System.Linq.IGrouping<,>"];
f = formatter.Replace("TREPLACE", typeArgs);
- var groupingInfo = new GenericSerializationInfo("global::System.Linq.IGrouping<" + typeArgs + ">", f, isOpenGenericType);
+ var groupingInfo = new GenericSerializationInfo(
+ "global::System.Linq.IGrouping<" + typeArgs + ">",
+ f,
+ genericTypeNamespace,
+ isOpenGenericType);
this.collectedGenericInfo.Add(groupingInfo);
formatter = KnownGenericTypes["System.Collections.Generic.IEnumerable<>"];
typeArgs = type.TypeArguments[1].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
f = formatter.Replace("TREPLACE", typeArgs);
- var enumerableInfo = new GenericSerializationInfo("global::System.Collections.Generic.IEnumerable<" + typeArgs + ">", f, isOpenGenericType);
+ var enumerableInfo = new GenericSerializationInfo(
+ "global::System.Collections.Generic.IEnumerable<" + typeArgs + ">",
+ f,
+ genericTypeNamespace,
+ isOpenGenericType);
this.collectedGenericInfo.Add(enumerableInfo);
return true;
}
@@ -590,11 +608,6 @@ private bool CollectGeneric(INamedTypeSymbol type)
}
var formatterBuilder = new StringBuilder();
- if (!type.ContainingNamespace.IsGlobalNamespace)
- {
- formatterBuilder.Append(type.ContainingNamespace.ToDisplayString() + ".");
- }
-
formatterBuilder.Append(type.Name);
formatterBuilder.Append("Formatter<");
var typeArgumentIterator = type.TypeArguments.GetEnumerator();
@@ -613,7 +626,11 @@ private bool CollectGeneric(INamedTypeSymbol type)
formatterBuilder.Append('>');
- var genericSerializationInfo = new GenericSerializationInfo(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), $"Formatters::{formatterBuilder}", isOpenGenericType);
+ var genericSerializationInfo = new GenericSerializationInfo(
+ type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
+ formatterBuilder.ToString(),
+ genericTypeNamespace,
+ isOpenGenericType);
this.collectedGenericInfo.Add(genericSerializationInfo);
return true;
}
@@ -663,7 +680,7 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
var intMembers = new Dictionary();
var stringMembers = new Dictionary();
- if (this.options.Generator.UsesMapMode || (contractAttr?.ConstructorArguments[0] is { Value: bool firstConstructorArgument } && firstConstructorArgument))
+ if (this.options.Generator.Formatters.UsesMapMode || (contractAttr?.ConstructorArguments[0] is { Value: bool firstConstructorArgument } && firstConstructorArgument))
{
// All public members are serialize target except [Ignore] member.
isIntKey = false;
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/UnionSerializationInfo.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/UnionSerializationInfo.cs
similarity index 71%
rename from src/MessagePack.Analyzers/CodeAnalysis/UnionSerializationInfo.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/UnionSerializationInfo.cs
index c0d3e906a..28df8b03d 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/UnionSerializationInfo.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/UnionSerializationInfo.cs
@@ -3,7 +3,7 @@
using Microsoft.CodeAnalysis;
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public record UnionSerializationInfo(
string? Namespace,
@@ -11,13 +11,9 @@ public record UnionSerializationInfo(
string FullName,
UnionSubTypeInfo[] SubTypes) : IResolverRegisterInfo
{
- public IReadOnlyCollection Diagnostics { get; init; } = Array.Empty();
+ public string FileNameHint => $"{CodeAnalysisUtilities.AppendNameToNamespace("Formatters", this.Namespace)}.{this.FormatterName}";
- public string FileNameHint => $"{CodeAnalysisUtilities.AppendNameToNamespace("Formatters", this.Namespace)}.{this.FormatterNameWithoutNamespace}";
-
- public string FormatterName => CodeAnalysisUtilities.QualifyWithOptionalNamespace(FormatterNameWithoutNamespace, $"Formatters::{this.Namespace}");
-
- public string FormatterNameWithoutNamespace => this.Name + "Formatter";
+ public string FormatterName => this.Name + "Formatter";
public virtual bool Equals(UnionSerializationInfo? other)
{
diff --git a/src/MessagePack.Analyzers/CodeAnalysis/UnionSubTypeInfo.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/UnionSubTypeInfo.cs
similarity index 80%
rename from src/MessagePack.Analyzers/CodeAnalysis/UnionSubTypeInfo.cs
rename to src/MessagePack.SourceGenerator/CodeAnalysis/UnionSubTypeInfo.cs
index a9e722776..88eaecaa1 100644
--- a/src/MessagePack.Analyzers/CodeAnalysis/UnionSubTypeInfo.cs
+++ b/src/MessagePack.SourceGenerator/CodeAnalysis/UnionSubTypeInfo.cs
@@ -1,6 +1,6 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-namespace MessagePack.Analyzers.CodeAnalysis;
+namespace MessagePack.SourceGenerator.CodeAnalysis;
public record UnionSubTypeInfo(int Key, string Type);
diff --git a/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.csproj b/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.csproj
index 04a0a95e4..5100b4a41 100644
--- a/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.csproj
+++ b/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.csproj
@@ -3,19 +3,19 @@
-
- true
- false
embedded
- false
- true
- $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs
- $(NoWarn);NU5128
-
- MessagePack Source Generator
- A roslyn source generator for AOT or faster startup of applications that use the MessagePack nuget package.
- MsgPack;MessagePack;Serialization;Formatter;Serializer
+ false
+
+
+
+
+
+
+
+
+
+
@@ -77,6 +77,10 @@
+
+
+
+
diff --git a/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.targets b/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.targets
index b621a73cd..810d10fe0 100644
--- a/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.targets
+++ b/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.targets
@@ -3,9 +3,7 @@
diff --git a/src/MessagePack.SourceGenerator/MessagePackGenerator.Emit.cs b/src/MessagePack.SourceGenerator/MessagePackGenerator.Emit.cs
index 4418028bc..930062d70 100644
--- a/src/MessagePack.SourceGenerator/MessagePackGenerator.Emit.cs
+++ b/src/MessagePack.SourceGenerator/MessagePackGenerator.Emit.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Text;
-using MessagePack.Analyzers.CodeAnalysis;
using MessagePack.SourceGenerator.Transforms;
using Microsoft.CodeAnalysis;
@@ -33,14 +32,12 @@ private static void Generate(IGeneratorContext context, FullModel model)
{
EnumTemplate transform = new(options, info);
AddTransform(transform.TransformText(), transform.FileName);
- TransferDiagnostics(info);
}
foreach (UnionSerializationInfo info in model.UnionInfos)
{
UnionTemplate transform = new(options, info);
AddTransform(transform.TransformText(), transform.FileName);
- TransferDiagnostics(info);
}
foreach (ObjectSerializationInfo info in model.ObjectInfos)
@@ -49,15 +46,6 @@ private static void Generate(IGeneratorContext context, FullModel model)
? new StringKeyFormatterTemplate(options, info)
: new FormatterTemplate(options, info);
AddTransform(transform.TransformText(), transform.FileName);
- TransferDiagnostics(info);
- }
-
- void TransferDiagnostics(IResolverRegisterInfo info)
- {
- foreach (Diagnostic diagnostic in info.Diagnostics)
- {
- context.ReportDiagnostic(diagnostic);
- }
}
void AddTransform(string transformOutput, string uniqueFileName)
diff --git a/src/MessagePack.SourceGenerator/MessagePackGenerator.cs b/src/MessagePack.SourceGenerator/MessagePackGenerator.cs
index 83af54f3a..8b736f803 100644
--- a/src/MessagePack.SourceGenerator/MessagePackGenerator.cs
+++ b/src/MessagePack.SourceGenerator/MessagePackGenerator.cs
@@ -2,32 +2,56 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Immutable;
-using MessagePack.Analyzers.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Diagnostics;
-using AnalyzerOptions = MessagePack.Analyzers.CodeAnalysis.AnalyzerOptions;
+using static MessagePack.SourceGenerator.Constants;
+using AnalyzerOptions = MessagePack.SourceGenerator.CodeAnalysis.AnalyzerOptions;
namespace MessagePack.SourceGenerator;
[Generator(LanguageNames.CSharp)]
public partial class MessagePackGenerator : IIncrementalGenerator
{
- public const string MessagePackObjectAttributeFullName = "MessagePack.MessagePackObjectAttribute";
- public const string MessagePackUnionAttributeFullName = "MessagePack.UnionAttribute";
-
public void Initialize(IncrementalGeneratorInitializationContext context)
{
- var options = context.AdditionalTextsProvider.Collect().Combine(context.AnalyzerConfigOptionsProvider).Select(
- ((ImmutableArray AdditionalFiles, AnalyzerConfigOptionsProvider Options) t, CancellationToken ct) => AnalyzerOptions.Parse(t.Options.GlobalOptions, t.AdditionalFiles, ct) with { IsGeneratingSource = true });
+ // TODO: Consider auto-detect formatters declared in this compilation
+ // so attributes are only required to use formatters from other assemblies.
+ var customFormatters = context.SyntaxProvider.ForAttributeWithMetadataName(
+ $"{AttributeNamespace}.{MessagePackKnownFormatterAttributeName}",
+ predicate: static (node, ct) => true,
+ transform: static (context, ct) => AnalyzerUtilities.ParseKnownFormatterAttribute(context.Attributes, ct)).Collect();
+
+ var customFormattedTypes = context.SyntaxProvider.ForAttributeWithMetadataName(
+ $"{AttributeNamespace}.{MessagePackAssumedFormattableAttributeName}",
+ predicate: static (node, ct) => true,
+ transform: static (context, ct) => AnalyzerUtilities.ParseAssumedFormattableAttribute(context.Attributes, ct)).SelectMany((a, ct) => a).Collect();
+
+ var resolverOptions = context.SyntaxProvider.ForAttributeWithMetadataName(
+ $"{AttributeNamespace}.{GeneratedMessagePackResolverAttributeName}",
+ predicate: static (node, ct) => true,
+ transform: static (context, ct) => AnalyzerUtilities.ParseGeneratorAttribute(context.Attributes, context.TargetSymbol, ct)).Collect().Select((a, ct) => a.SingleOrDefault(ao => ao is not null));
+
+ var options = resolverOptions.Combine(customFormattedTypes).Combine(customFormatters).Select(static (input, ct) =>
+ {
+ AnalyzerOptions? options = input.Left.Left ?? new();
+
+ var formattableTypes = input.Left.Right;
+ var formatterTypes = input.Right.Aggregate(
+ ImmutableDictionary>.Empty,
+ (first, second) => first.AddRange(second));
+
+ options = options.WithFormatterTypes(formattableTypes, formatterTypes);
+
+ return options;
+ });
var messagePackObjectTypes = context.SyntaxProvider.ForAttributeWithMetadataName(
- MessagePackObjectAttributeFullName,
+ $"{AttributeNamespace}.{MessagePackObjectAttributeName}",
predicate: static (node, _) => node is TypeDeclarationSyntax,
transform: static (context, _) => (TypeDeclarationSyntax)context.TargetNode);
var unionTypes = context.SyntaxProvider.ForAttributeWithMetadataName(
- MessagePackUnionAttributeFullName,
+ $"{AttributeNamespace}.{MessagePackUnionAttributeName}",
predicate: static (node, _) => node is InterfaceDeclarationSyntax,
transform: static (context, _) => (TypeDeclarationSyntax)context.TargetNode);
@@ -39,15 +63,17 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
.Combine(options)
.Select(static (s, ct) =>
{
+ AnalyzerOptions options = s.Right;
+
if (!ReferenceSymbols.TryCreate(s.Left.Right, out ReferenceSymbols? referenceSymbols))
{
- return null;
+ return default;
}
List modelPerType = new();
void Collect(TypeDeclarationSyntax typeDecl)
{
- if (TypeCollector.Collect(s.Left.Right, s.Right, referenceSymbols, reportDiagnostic: null, typeDecl, ct) is FullModel model)
+ if (TypeCollector.Collect(s.Left.Right, options, referenceSymbols, reportAnalyzerDiagnostic: null, typeDecl, ct) is FullModel model)
{
modelPerType.Add(model);
}
@@ -68,17 +94,17 @@ void Collect(TypeDeclarationSyntax typeDecl)
context.RegisterSourceOutput(source, static (context, source) =>
{
- if (source is not null)
+ if (source is { Options.IsGeneratingSource: true })
{
- Generate(new GeneratorContext(context), source);
+ GenerateResolver(new GeneratorContext(context), source);
}
});
context.RegisterSourceOutput(source, static (context, source) =>
{
- if (source is not null)
+ if (source is { Options.IsGeneratingSource: true })
{
- GenerateResolver(new GeneratorContext(context), source);
+ Generate(new GeneratorContext(context), source);
}
});
}
diff --git a/src/MessagePack.Analyzers/Properties/AssemblyInfo.cs b/src/MessagePack.SourceGenerator/Properties/AssemblyInfo.cs
similarity index 73%
rename from src/MessagePack.Analyzers/Properties/AssemblyInfo.cs
rename to src/MessagePack.SourceGenerator/Properties/AssemblyInfo.cs
index 0c458607b..9ae3dbb6b 100644
--- a/src/MessagePack.Analyzers/Properties/AssemblyInfo.cs
+++ b/src/MessagePack.SourceGenerator/Properties/AssemblyInfo.cs
@@ -4,3 +4,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MessagePack.Analyzers.CodeFixes, PublicKey=" + ThisAssembly.PublicKey)]
+[assembly: InternalsVisibleTo("MessagePack.Analyzers.CodeFixes.Unity, PublicKey=" + ThisAssembly.PublicKey)]
diff --git a/src/MessagePack.Analyzers/Strings.resx b/src/MessagePack.SourceGenerator/Strings.resx
similarity index 100%
rename from src/MessagePack.Analyzers/Strings.resx
rename to src/MessagePack.SourceGenerator/Strings.resx
diff --git a/src/MessagePack.SourceGenerator/Transforms/EnumTemplate.cs b/src/MessagePack.SourceGenerator/Transforms/EnumTemplate.cs
index 2ddc5bc36..4f9ac1b88 100644
--- a/src/MessagePack.SourceGenerator/Transforms/EnumTemplate.cs
+++ b/src/MessagePack.SourceGenerator/Transforms/EnumTemplate.cs
@@ -2,7 +2,7 @@
//
// This code was generated by a tool.
// Runtime Version: 17.0.0.0
-//
+//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
@@ -10,7 +10,7 @@
namespace MessagePack.SourceGenerator.Transforms
{
using System;
-
+
///
/// Class to produce the template output
///
@@ -22,28 +22,39 @@ public partial class EnumTemplate : EnumTemplateBase
///
public virtual string TransformText()
{
- this.Write("\r\nnamespace ");
- this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));
- this.Write("\r\n{\r\n\tusing MsgPack = global::MessagePack;\r\n\tusing ");
- this.Write(this.ToStringHelper.ToStringWithCulture(Info.Name));
- this.Write(" = ");
- this.Write(this.ToStringHelper.ToStringWithCulture(Info.FullName));
- this.Write(";\r\n\r\n\tinternal sealed class ");
+ this.Write("\r\n");
+ if (ResolverNamespace.Length > 0) {
+ this.Write("namespace ");
+ this.Write(this.ToStringHelper.ToStringWithCulture(ResolverNamespace));
+ this.Write(" {\r\n");
+ }
+ this.Write("\r\nusing MsgPack = global::MessagePack;\r\n\r\npartial class ");
+ this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
+ this.Write("\r\n{\r\n");
+ using (this.EmitClassesForNamespace(out string classVisibility, this.Write)) {
+ this.Write("\t");
+ this.Write(this.ToStringHelper.ToStringWithCulture(classVisibility));
+ this.Write(" sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.Name));
this.Write("Formatter : MsgPack::Formatters.IMessagePackFormatter<");
- this.Write(this.ToStringHelper.ToStringWithCulture(Info.Name));
+ this.Write(this.ToStringHelper.ToStringWithCulture(Info.FullName));
this.Write(">\r\n\t{\r\n\t\tpublic void Serialize(ref MsgPack::MessagePackWriter writer, ");
- this.Write(this.ToStringHelper.ToStringWithCulture(Info.Name));
+ this.Write(this.ToStringHelper.ToStringWithCulture(Info.FullName));
this.Write(" value, MsgPack::MessagePackSerializerOptions options)\r\n\t\t{\r\n\t\t\twriter.Write((");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.UnderlyingTypeKeyword));
this.Write(")value);\r\n\t\t}\r\n\r\n\t\tpublic ");
- this.Write(this.ToStringHelper.ToStringWithCulture(Info.Name));
+ this.Write(this.ToStringHelper.ToStringWithCulture(Info.FullName));
this.Write(" Deserialize(ref MsgPack::MessagePackReader reader, MsgPack::MessagePackSerialize" +
"rOptions options)\r\n\t\t{\r\n\t\t\treturn (");
- this.Write(this.ToStringHelper.ToStringWithCulture(Info.Name));
+ this.Write(this.ToStringHelper.ToStringWithCulture(Info.FullName));
this.Write(")reader.Read");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.UnderlyingTypeName));
- this.Write("();\r\n\t\t}\r\n\t}\r\n}\r\n");
+ this.Write("();\r\n\t\t}\r\n\t}\r\n");
+ }
+ this.Write("}\r\n\r\n");
+ if (ResolverNamespace.Length > 0) {
+ this.Write("}\r\n");
+ }
return this.GenerationEnvironment.ToString();
}
}
@@ -146,7 +157,7 @@ public void Write(string textToAppend)
}
// If we're starting off, or if the previous text ended with a newline,
// we have to append the current indent first.
- if (((this.GenerationEnvironment.Length == 0)
+ if (((this.GenerationEnvironment.Length == 0)
|| this.endsWithNewline))
{
this.GenerationEnvironment.Append(this.currentIndentField);
diff --git a/src/MessagePack.SourceGenerator/Transforms/EnumTemplate.tt b/src/MessagePack.SourceGenerator/Transforms/EnumTemplate.tt
index 1e6a20e88..e185871b3 100644
--- a/src/MessagePack.SourceGenerator/Transforms/EnumTemplate.tt
+++ b/src/MessagePack.SourceGenerator/Transforms/EnumTemplate.tt
@@ -1,21 +1,30 @@
<#@ template debug="false" hostspecific="false" linePragmas="false" language="C#" #>
<#@ assembly name="System.Core" #>
-namespace <#= Namespace #>
-{
- using MsgPack = global::MessagePack;
- using <#= Info.Name #> = <#= Info.FullName #>;
+<# if (ResolverNamespace.Length > 0) { #>
+namespace <#= ResolverNamespace #> {
+<# } #>
+
+using MsgPack = global::MessagePack;
- internal sealed class <#= Info.Name #>Formatter : MsgPack::Formatters.IMessagePackFormatter<<#= Info.Name #>>
+partial class <#= ResolverName #>
+{
+<# using (this.EmitClassesForNamespace(out string classVisibility, this.Write)) { #>
+ <#= classVisibility #> sealed class <#= Info.Name #>Formatter : MsgPack::Formatters.IMessagePackFormatter<<#= Info.FullName #>>
{
- public void Serialize(ref MsgPack::MessagePackWriter writer, <#= Info.Name #> value, MsgPack::MessagePackSerializerOptions options)
+ public void Serialize(ref MsgPack::MessagePackWriter writer, <#= Info.FullName #> value, MsgPack::MessagePackSerializerOptions options)
{
writer.Write((<#= Info.UnderlyingTypeKeyword #>)value);
}
- public <#= Info.Name #> Deserialize(ref MsgPack::MessagePackReader reader, MsgPack::MessagePackSerializerOptions options)
+ public <#= Info.FullName #> Deserialize(ref MsgPack::MessagePackReader reader, MsgPack::MessagePackSerializerOptions options)
{
- return (<#= Info.Name #>)reader.Read<#= Info.UnderlyingTypeName #>();
+ return (<#= Info.FullName #>)reader.Read<#= Info.UnderlyingTypeName #>();
}
}
+<# } #>
+}
+
+<# if (ResolverNamespace.Length > 0) { #>
}
+<# } #>
diff --git a/src/MessagePack.SourceGenerator/Transforms/FormatterTemplate.cs b/src/MessagePack.SourceGenerator/Transforms/FormatterTemplate.cs
index 9de8f7bd3..f859daa4e 100644
--- a/src/MessagePack.SourceGenerator/Transforms/FormatterTemplate.cs
+++ b/src/MessagePack.SourceGenerator/Transforms/FormatterTemplate.cs
@@ -12,7 +12,6 @@ namespace MessagePack.SourceGenerator.Transforms
using System.Linq;
using System.Text;
using System.Collections.Generic;
- using MessagePack.Analyzers.Transforms;
using System;
///
@@ -27,12 +26,21 @@ public partial class FormatterTemplate : FormatterTemplateBase
public virtual string TransformText()
{
this.Write("\r\n#pragma warning disable CS8669 // We may leak nullable annotations into generat" +
- "ed code.\r\n\r\nnamespace ");
- this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));
- this.Write("\r\n{\r\n\tusing MsgPack = global::MessagePack;\r\n\r\n");
- bool isFormatterResolverNecessary = ShouldUseFormatterResolverHelper.ShouldUseFormatterResolver(Info.Members);
- this.Write("\tinternal sealed class ");
- this.Write(this.ToStringHelper.ToStringWithCulture(Info.FormatterNameWithoutNamespace));
+ "ed code.\r\n\r\n");
+ if (ResolverNamespace.Length > 0) {
+ this.Write("namespace ");
+ this.Write(this.ToStringHelper.ToStringWithCulture(ResolverNamespace));
+ this.Write(" {\r\n");
+ }
+ this.Write("\r\nusing MsgPack = global::MessagePack;\r\n\r\npartial class ");
+ this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
+ this.Write("\r\n{\r\n");
+ bool isFormatterResolverNecessary = GeneratorUtilities.ShouldUseFormatterResolver(Info.Members);
+ using (this.EmitClassesForNamespace(out string classVisibility, this.Write)) {
+ this.Write("\t");
+ this.Write(this.ToStringHelper.ToStringWithCulture(classVisibility));
+ this.Write(" sealed class ");
+ this.Write(this.ToStringHelper.ToStringWithCulture(Info.FormatterName));
this.Write(" : MsgPack::Formatters.IMessagePackFormatter<");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.FullName));
this.Write(">\r\n");
@@ -182,7 +190,12 @@ public virtual string TransformText()
}
this.Write("\t\t\treader.Depth--;\r\n\t\t\treturn ____result;\r\n");
}
- this.Write("\t\t}\r\n\t}\r\n}\r\n");
+ this.Write("\t\t}\r\n\t}\r\n\r\n");
+ } // close EmitClassesForNamespace
+ this.Write("}\r\n\r\n");
+ if (ResolverNamespace.Length > 0) {
+ this.Write("}\r\n");
+ }
return this.GenerationEnvironment.ToString();
}
}
diff --git a/src/MessagePack.SourceGenerator/Transforms/FormatterTemplate.tt b/src/MessagePack.SourceGenerator/Transforms/FormatterTemplate.tt
index 5da3af86c..08380f9fe 100644
--- a/src/MessagePack.SourceGenerator/Transforms/FormatterTemplate.tt
+++ b/src/MessagePack.SourceGenerator/Transforms/FormatterTemplate.tt
@@ -3,16 +3,20 @@
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
-<#@ import namespace="MessagePackAnalyzer.Transforms" #>
#pragma warning disable CS8669 // We may leak nullable annotations into generated code.
-namespace <#= Namespace #>
-{
- using MsgPack = global::MessagePack;
+<# if (ResolverNamespace.Length > 0) { #>
+namespace <#= ResolverNamespace #> {
+<# } #>
-<# bool isFormatterResolverNecessary = ShouldUseFormatterResolverHelper.ShouldUseFormatterResolver(Info.Members);#>
- internal sealed class <#= Info.FormatterNameWithoutNamespace #> : MsgPack::Formatters.IMessagePackFormatter<<#= Info.FullName #>>
+using MsgPack = global::MessagePack;
+
+partial class <#= ResolverName #>
+{
+<# bool isFormatterResolverNecessary = GeneratorUtilities.ShouldUseFormatterResolver(Info.Members);#>
+<# using (this.EmitClassesForNamespace(out string classVisibility, this.Write)) { #>
+ <#=classVisibility#> sealed class <#= Info.FormatterName #> : MsgPack::Formatters.IMessagePackFormatter<<#= Info.FullName #>>
<# foreach (var typeArg in Info.GenericTypeParameters.Where(x => x.HasConstraints)) { #>
where <#= typeArg.Name #> : <#= typeArg.Constraints #>
<# } #>
@@ -141,4 +145,10 @@ namespace <#= Namespace #>
<# } #>
}
}
+
+<# } // close EmitClassesForNamespace #>
+}
+
+<# if (ResolverNamespace.Length > 0) { #>
}
+<# } #>
diff --git a/src/MessagePack.SourceGenerator/Transforms/GeneratorUtilities.cs b/src/MessagePack.SourceGenerator/Transforms/GeneratorUtilities.cs
new file mode 100644
index 000000000..35589714a
--- /dev/null
+++ b/src/MessagePack.SourceGenerator/Transforms/GeneratorUtilities.cs
@@ -0,0 +1,20 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace MessagePack.SourceGenerator.Transforms;
+
+internal static class GeneratorUtilities
+{
+ internal static bool ShouldUseFormatterResolver(MemberSerializationInfo[] infos)
+ {
+ foreach (var memberSerializationInfo in infos)
+ {
+ if (memberSerializationInfo.CustomFormatterTypeName == null && Array.IndexOf(AnalyzerUtilities.PrimitiveTypes, memberSerializationInfo.Type) == -1)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/MessagePack.SourceGenerator/Transforms/IFormatterTemplate.cs b/src/MessagePack.SourceGenerator/Transforms/IFormatterTemplate.cs
index 77e417ad3..ba1b1bd66 100644
--- a/src/MessagePack.SourceGenerator/Transforms/IFormatterTemplate.cs
+++ b/src/MessagePack.SourceGenerator/Transforms/IFormatterTemplate.cs
@@ -1,17 +1,23 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using MessagePack.Analyzers.CodeAnalysis;
-
namespace MessagePack.SourceGenerator.Transforms;
public interface IFormatterTemplate
{
string FileName { get; }
- string Namespace { get; }
+ ///
+ /// Gets the namespace of the formatter type.
+ ///
+ ///
+ /// This must not begin with global::.
+ ///
+ string? FormattedTypeNamespace { get; }
+
+ string ResolverNamespace { get; }
- ObjectSerializationInfo Info { get; }
+ string ResolverName { get; }
string TransformText();
}
diff --git a/src/MessagePack.SourceGenerator/Transforms/ResolverTemplate.cs b/src/MessagePack.SourceGenerator/Transforms/ResolverTemplate.cs
index f1ff98243..a8d493c5d 100644
--- a/src/MessagePack.SourceGenerator/Transforms/ResolverTemplate.cs
+++ b/src/MessagePack.SourceGenerator/Transforms/ResolverTemplate.cs
@@ -2,7 +2,7 @@
//
// This code was generated by a tool.
// Runtime Version: 17.0.0.0
-//
+//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
@@ -13,7 +13,7 @@ namespace MessagePack.SourceGenerator.Transforms
using System.Text;
using System.Collections.Generic;
using System;
-
+
///
/// Class to produce the template output
///
@@ -25,46 +25,47 @@ public partial class ResolverTemplate : ResolverTemplateBase
///
public virtual string TransformText()
{
- this.Write("\r\nnamespace ");
+ this.Write("\r\n");
+ if (ResolverNamespace.Length > 0) {
+ this.Write("namespace ");
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverNamespace));
- this.Write("\r\n{\r\n\tusing MsgPack = global::MessagePack;\r\n\tusing Formatters = global::");
- this.Write(this.ToStringHelper.ToStringWithCulture(FormatterNamespace));
- this.Write(";\r\n\r\n\t/// A MessagePack resolver that uses generated formatters for type" +
- "s in this assembly.\r\n\t");
- this.Write(this.ToStringHelper.ToStringWithCulture(PublicResolver ? "public" : "internal"));
- this.Write(" class ");
+ this.Write(" {\r\n");
+ }
+ this.Write("\r\nusing MsgPack = global::MessagePack;\r\n\r\n/// A MessagePack resolver tha" +
+ "t uses generated formatters for types in this assembly.\r\npartial class" +
+ " ");
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
- this.Write(" : MsgPack::IFormatterResolver\r\n\t{\r\n\t\t/// An instance of this resolver t" +
- "hat only returns formatters specifically generated for types in this assembly." +
- "summary>\r\n\t\tpublic static readonly MsgPack::IFormatterResolver Instance = new ");
+ this.Write(" : MsgPack::IFormatterResolver\r\n{\r\n\t/// An instance of this resolver tha" +
+ "t only returns formatters specifically generated for types in this assembly.\r\n\tpublic static readonly MsgPack::IFormatterResolver Instance = new ");
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
this.Write(@"();
- /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = MsgPack::Resolvers.CompositeResolver.Create(Instance, MsgPack::Resolvers.StandardAotResolver.Instance);
+ /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = new WithStandardAotResolver();
- private ");
+ private ");
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
this.Write(@"()
- {
- }
+ {
+ }
- public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
- {
- return FormatterCache.Formatter;
- }
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
- private static class FormatterCache
- {
- internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
- static FormatterCache()
- {
- var f = ");
+ static FormatterCache()
+ {
+ var f = ");
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
- this.Write("GetFormatterHelper.GetFormatter(typeof(T));\r\n\t\t\t\tif (f != null)\r\n\t\t\t\t{\r\n\t\t\t\t\tForm" +
- "atter = (MsgPack::Formatters.IMessagePackFormatter)f;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n" +
- "\r\n\tinternal static class ");
+ this.Write("GetFormatterHelper.GetFormatter(typeof(T));\r\n\t\t\tif (f != null)\r\n\t\t\t{\r\n\t\t\t\tFormatt" +
+ "er = (MsgPack::Formatters.IMessagePackFormatter)f;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate" +
+ " static class ");
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
this.Write("GetFormatterHelper\r\n\t{\r\n\t\tprivate static readonly global::System.Collections.Gene" +
"ric.Dictionary lookup;\r\n\r\n\t\tstatic ");
@@ -72,25 +73,47 @@ static FormatterCache()
this.Write("GetFormatterHelper()\r\n\t\t{\r\n\t\t\tlookup = new global::System.Collections.Generic.Dic" +
"tionary(");
this.Write(this.ToStringHelper.ToStringWithCulture(RegisterInfos.Count));
- this.Write(")\r\n\t\t\t{\r\n");
- for(var i = 0; i < RegisterInfos.Count; i++) { var x = RegisterInfos[i];
+ this.Write(")\r\n\t\t\t{\r\n\t");
+ for(var i = 0; i < RegisterInfos.Count; i++) { var x = RegisterInfos[i];
this.Write("\t\t\t\t{ typeof(");
this.Write(this.ToStringHelper.ToStringWithCulture(x.FullName));
this.Write("), ");
this.Write(this.ToStringHelper.ToStringWithCulture(i));
- this.Write(" },\r\n");
- }
+ this.Write(" },\r\n\t");
+ }
this.Write("\t\t\t};\r\n\t\t}\r\n\r\n\t\tinternal static object GetFormatter(global::System.Type t)\r\n\t\t{\r\n" +
"\t\t\tint key;\r\n\t\t\tif (!lookup.TryGetValue(t, out key))\r\n\t\t\t{\r\n\t\t\t\treturn null;\r\n\t\t" +
- "\t}\r\n\r\n\t\t\tswitch (key)\r\n\t\t\t{\r\n");
- for(var i = 0; i < RegisterInfos.Count; i++) { var x = RegisterInfos[i];
+ "\t}\r\n\r\n\t\t\tswitch (key)\r\n\t\t\t{\r\n\t");
+ for(var i = 0; i < RegisterInfos.Count; i++) { var x = RegisterInfos[i];
this.Write("\t\t\t\tcase ");
this.Write(this.ToStringHelper.ToStringWithCulture(i));
this.Write(": return new ");
- this.Write(this.ToStringHelper.ToStringWithCulture(x.FormatterName));
- this.Write("();\r\n");
- }
- this.Write("\t\t\t\tdefault: return null;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n");
+ this.Write(this.ToStringHelper.ToStringWithCulture(CodeAnalysisUtilities.QualifyWithOptionalNamespace(x.FormatterName, x.Namespace)));
+ this.Write("();\r\n\t");
+ }
+ this.Write(@" default: return null;
+ }
+ }
+ }
+
+ private class WithStandardAotResolver : MsgPack::IFormatterResolver
+ {
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
+
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter = Instance.GetFormatter() ?? MsgPack::Resolvers.StandardAotResolver.Instance.GetFormatter();
+ }
+ }
+}
+
+");
+ if (ResolverNamespace.Length > 0) {
+ this.Write("}\r\n");
+ }
return this.GenerationEnvironment.ToString();
}
}
@@ -193,7 +216,7 @@ public void Write(string textToAppend)
}
// If we're starting off, or if the previous text ended with a newline,
// we have to append the current indent first.
- if (((this.GenerationEnvironment.Length == 0)
+ if (((this.GenerationEnvironment.Length == 0)
|| this.endsWithNewline))
{
this.GenerationEnvironment.Append(this.currentIndentField);
diff --git a/src/MessagePack.SourceGenerator/Transforms/ResolverTemplate.tt b/src/MessagePack.SourceGenerator/Transforms/ResolverTemplate.tt
index 8334a638f..98afb4465 100644
--- a/src/MessagePack.SourceGenerator/Transforms/ResolverTemplate.tt
+++ b/src/MessagePack.SourceGenerator/Transforms/ResolverTemplate.tt
@@ -4,45 +4,45 @@
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
-namespace <#= ResolverNamespace #>
+<# if (ResolverNamespace.Length > 0) { #>
+namespace <#= ResolverNamespace #> {
+<# } #>
+
+using MsgPack = global::MessagePack;
+
+/// A MessagePack resolver that uses generated formatters for types in this assembly.
+partial class <#= ResolverName #> : MsgPack::IFormatterResolver
{
- using MsgPack = global::MessagePack;
- using Formatters = global::<#= FormatterNamespace #>;
+ /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver Instance = new <#= ResolverName #>();
- /// A MessagePack resolver that uses generated formatters for types in this assembly.
- <#= PublicResolver ? "public" : "internal" #> class <#= ResolverName #> : MsgPack::IFormatterResolver
- {
- /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver Instance = new <#= ResolverName #>();
+ /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = new WithStandardAotResolver();
- /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = MsgPack::Resolvers.CompositeResolver.Create(Instance, MsgPack::Resolvers.StandardAotResolver.Instance);
+ private <#= ResolverName #>()
+ {
+ }
- private <#= ResolverName #>()
- {
- }
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
- public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
- {
- return FormatterCache.Formatter;
- }
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
- private static class FormatterCache
+ static FormatterCache()
{
- internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
-
- static FormatterCache()
+ var f = <#= ResolverName #>GetFormatterHelper.GetFormatter(typeof(T));
+ if (f != null)
{
- var f = <#= ResolverName #>GetFormatterHelper.GetFormatter(typeof(T));
- if (f != null)
- {
- Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
- }
+ Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
}
}
}
- internal static class <#= ResolverName #>GetFormatterHelper
+ private static class <#= ResolverName #>GetFormatterHelper
{
private static readonly global::System.Collections.Generic.Dictionary lookup;
@@ -50,9 +50,9 @@ namespace <#= ResolverNamespace #>
{
lookup = new global::System.Collections.Generic.Dictionary(<#= RegisterInfos.Count #>)
{
-<# for(var i = 0; i < RegisterInfos.Count; i++) { var x = RegisterInfos[i]; #>
+ <# for(var i = 0; i < RegisterInfos.Count; i++) { var x = RegisterInfos[i]; #>
{ typeof(<#= x.FullName #>), <#= i #> },
-<# } #>
+ <# } #>
};
}
@@ -66,11 +66,28 @@ namespace <#= ResolverNamespace #>
switch (key)
{
-<# for(var i = 0; i < RegisterInfos.Count; i++) { var x = RegisterInfos[i]; #>
- case <#= i #>: return new <#= x.FormatterName #>();
-<# } #>
+ <# for(var i = 0; i < RegisterInfos.Count; i++) { var x = RegisterInfos[i]; #>
+ case <#= i #>: return new <#= CodeAnalysisUtilities.QualifyWithOptionalNamespace(x.FormatterName, x.Namespace) #>();
+ <# } #>
default: return null;
}
}
}
+
+ private class WithStandardAotResolver : MsgPack::IFormatterResolver
+ {
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
+
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter = Instance.GetFormatter() ?? MsgPack::Resolvers.StandardAotResolver.Instance.GetFormatter();
+ }
+ }
+}
+
+<# if (ResolverNamespace.Length > 0) { #>
}
+<# } #>
diff --git a/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterDeserializeHelper.cs b/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterDeserializeHelper.cs
index 8d3fa512d..107ea1571 100644
--- a/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterDeserializeHelper.cs
+++ b/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterDeserializeHelper.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Text;
-using MessagePack.Analyzers.CodeAnalysis;
using MessagePack.Internal;
namespace MessagePack.SourceGenerator.Transforms;
diff --git a/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterTemplate.cs b/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterTemplate.cs
index 565dd0aa3..39c1a03a2 100644
--- a/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterTemplate.cs
+++ b/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterTemplate.cs
@@ -12,8 +12,7 @@ namespace MessagePack.SourceGenerator.Transforms
using System;
using System.Linq;
using System.Collections.Generic;
- using MessagePack.Analyzers.CodeAnalysis;
- using MessagePack.Analyzers.Transforms;
+ using MessagePack.SourceGenerator.CodeAnalysis;
///
/// Class to produce the template output
@@ -26,18 +25,27 @@ public partial class StringKeyFormatterTemplate : StringKeyFormatterTemplateBase
///
public virtual string TransformText()
{
- this.Write("\r\nnamespace ");
- this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));
- this.Write("\r\n{\r\n\tusing MsgPack = global::MessagePack;\r\n\r\n");
+ this.Write("\r\n");
+ if (ResolverNamespace.Length > 0) {
+ this.Write("namespace ");
+ this.Write(this.ToStringHelper.ToStringWithCulture(ResolverNamespace));
+ this.Write(" {\r\n");
+ }
+ this.Write("\r\nusing MsgPack = global::MessagePack;\r\n\r\npartial class ");
+ this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
+ this.Write("\r\n{\r\n");
var list = new List>();
foreach (var member in Info.Members) {
var binary = EmbedStringHelper.Utf8.GetBytes(member.StringKey);
list.Add(new ValueTuple(member, binary));
}
- bool isFormatterResolverNecessary = ShouldUseFormatterResolverHelper.ShouldUseFormatterResolver(Info.Members);
- this.Write("\tinternal sealed class ");
- this.Write(this.ToStringHelper.ToStringWithCulture(Info.FormatterNameWithoutNamespace));
+ bool isFormatterResolverNecessary = GeneratorUtilities.ShouldUseFormatterResolver(Info.Members);
+ using (this.EmitClassesForNamespace(out string classVisibility, this.Write)) {
+ this.Write("\t");
+ this.Write(this.ToStringHelper.ToStringWithCulture(classVisibility));
+ this.Write(" sealed class ");
+ this.Write(this.ToStringHelper.ToStringWithCulture(Info.FormatterName));
this.Write(" : global::MessagePack.Formatters.IMessagePackFormatter<");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.FullName));
this.Write(">\r\n");
@@ -177,7 +185,12 @@ public virtual string TransformText()
if (Info.Members.Length != 0) {
this.Write("\t\t\treader.Depth--;\r\n");
}
- this.Write("\t\t\treturn ____result;\r\n\t\t}\r\n\t}\r\n}\r\n");
+ this.Write("\t\t\treturn ____result;\r\n\t\t}\r\n\t}\r\n");
+ } // close EmitClassesForNamespace
+ this.Write("}\r\n\r\n");
+ if (ResolverNamespace.Length > 0) {
+ this.Write("}\r\n");
+ }
return this.GenerationEnvironment.ToString();
}
}
diff --git a/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterTemplate.tt b/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterTemplate.tt
index 33421f2bd..fd515a331 100644
--- a/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterTemplate.tt
+++ b/src/MessagePack.SourceGenerator/Transforms/StringKey/StringKeyFormatterTemplate.tt
@@ -3,21 +3,25 @@
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
-<#@ import namespace="MessagePackAnalyzer.CodeAnalysis" #>
-<#@ import namespace="MessagePackAnalyzer.Transforms" #>
+<#@ import namespace="MessagePack.SourceGenerator.CodeAnalysis" #>
-namespace <#= Namespace #>
-{
- using MsgPack = global::MessagePack;
+<# if (ResolverNamespace.Length > 0) { #>
+namespace <#= ResolverNamespace #> {
+<# } #>
+using MsgPack = global::MessagePack;
+
+partial class <#= ResolverName #>
+{
<# var list = new List>();
foreach (var member in Info.Members) {
var binary = EmbedStringHelper.Utf8.GetBytes(member.StringKey);
list.Add(new ValueTuple(member, binary));
}
- bool isFormatterResolverNecessary = ShouldUseFormatterResolverHelper.ShouldUseFormatterResolver(Info.Members); #>
- internal sealed class <#= Info.FormatterNameWithoutNamespace #> : global::MessagePack.Formatters.IMessagePackFormatter<<#= Info.FullName #>>
+ bool isFormatterResolverNecessary = GeneratorUtilities.ShouldUseFormatterResolver(Info.Members); #>
+<# using (this.EmitClassesForNamespace(out string classVisibility, this.Write)) { #>
+ <#= classVisibility #> sealed class <#= Info.FormatterName #> : global::MessagePack.Formatters.IMessagePackFormatter<<#= Info.FullName #>>
<# foreach (var typeArg in Info.GenericTypeParameters.Where(x => x.HasConstraints)) {#>
where <#= typeArg.Name #> : <#= typeArg.Constraints #>
<# }#>
@@ -135,4 +139,9 @@ namespace <#= Namespace #>
return ____result;
}
}
+<# } // close EmitClassesForNamespace #>
}
+
+<# if (ResolverNamespace.Length > 0) { #>
+}
+<# } #>
diff --git a/src/MessagePack.SourceGenerator/Transforms/TemplatePartials.cs b/src/MessagePack.SourceGenerator/Transforms/TemplatePartials.cs
index 25a4df910..47a23faba 100644
--- a/src/MessagePack.SourceGenerator/Transforms/TemplatePartials.cs
+++ b/src/MessagePack.SourceGenerator/Transforms/TemplatePartials.cs
@@ -3,23 +3,24 @@
#pragma warning disable SA1402 // File may only contain a single type
-using MessagePack.Analyzers.CodeAnalysis;
-
namespace MessagePack.SourceGenerator.Transforms;
public partial class FormatterTemplate : IFormatterTemplate
{
public FormatterTemplate(AnalyzerOptions options, ObjectSerializationInfo info)
{
- this.Namespace = CodeAnalysisUtilities.AppendNameToNamespace(options.FormatterNamespace, info.Namespace);
this.Options = options;
this.Info = info;
}
- public string Namespace { get; }
-
public AnalyzerOptions Options { get; }
+ public string? FormattedTypeNamespace => this.Info.Namespace;
+
+ public string ResolverNamespace => this.Options.Generator.Resolver.Namespace ?? string.Empty;
+
+ public string ResolverName => this.Options.Generator.Resolver.Name;
+
public ObjectSerializationInfo Info { get; }
public string FileName => $"{this.Info.FileNameHint}.g.cs";
@@ -29,15 +30,18 @@ public partial class StringKeyFormatterTemplate : IFormatterTemplate
{
public StringKeyFormatterTemplate(AnalyzerOptions options, ObjectSerializationInfo info)
{
- this.Namespace = CodeAnalysisUtilities.AppendNameToNamespace(options.FormatterNamespace, info.Namespace);
this.Options = options;
this.Info = info;
}
- public string Namespace { get; }
-
public AnalyzerOptions Options { get; }
+ public string? FormattedTypeNamespace => this.Info.Namespace;
+
+ public string ResolverNamespace => this.Options.Generator.Resolver.Namespace ?? string.Empty;
+
+ public string ResolverName => this.Options.Generator.Resolver.Name;
+
public ObjectSerializationInfo Info { get; }
public string FileName => $"{this.Info.FileNameHint}.g.cs";
@@ -55,48 +59,50 @@ public ResolverTemplate(AnalyzerOptions options, IReadOnlyList this.Options.Generator.Resolver.Namespace ?? string.Empty;
- public string FormatterNamespace => this.Options.FormatterNamespace;
-
public string ResolverName => this.Options.Generator.Resolver.Name;
- public bool PublicResolver => this.Options.Generator.Resolver.Public;
-
public IReadOnlyList RegisterInfos { get; }
public string FileName => $"{CodeAnalysisUtilities.QualifyWithOptionalNamespace(this.ResolverName, this.ResolverNamespace)}.g.cs";
}
-public partial class EnumTemplate
+public partial class EnumTemplate : IFormatterTemplate
{
public EnumTemplate(AnalyzerOptions options, EnumSerializationInfo info)
{
- this.Namespace = CodeAnalysisUtilities.AppendNameToNamespace(options.FormatterNamespace, info.Namespace);
this.Options = options;
this.Info = info;
}
- public string Namespace { get; }
-
public AnalyzerOptions Options { get; }
+ public string? FormattedTypeNamespace => this.Info.Namespace;
+
+ public string ResolverNamespace => this.Options.Generator.Resolver.Namespace ?? string.Empty;
+
+ public string ResolverName => this.Options.Generator.Resolver.Name;
+
public EnumSerializationInfo Info { get; }
public string FileName => $"{this.Info.FileNameHint}.g.cs";
}
-public partial class UnionTemplate
+public partial class UnionTemplate : IFormatterTemplate
{
public UnionTemplate(AnalyzerOptions options, UnionSerializationInfo info)
{
- this.Namespace = CodeAnalysisUtilities.AppendNameToNamespace(options.FormatterNamespace, info.Namespace);
this.Options = options;
this.Info = info;
}
- public string Namespace { get; }
-
public AnalyzerOptions Options { get; }
+ public string? FormattedTypeNamespace => this.Info.Namespace;
+
+ public string ResolverNamespace => this.Options.Generator.Resolver.Namespace ?? string.Empty;
+
+ public string ResolverName => this.Options.Generator.Resolver.Name;
+
public UnionSerializationInfo Info { get; }
public string FileName => $"{this.Info.FileNameHint}.g.cs";
diff --git a/src/MessagePack.SourceGenerator/Transforms/TransformUtilities.cs b/src/MessagePack.SourceGenerator/Transforms/TransformUtilities.cs
new file mode 100644
index 000000000..fee165428
--- /dev/null
+++ b/src/MessagePack.SourceGenerator/Transforms/TransformUtilities.cs
@@ -0,0 +1,52 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace MessagePack.SourceGenerator.Transforms;
+
+internal static class TransformUtilities
+{
+ internal static IDisposable? EmitClassesForNamespace(this IFormatterTemplate template, out string formatterVisibility, Action writer)
+ {
+ string? ns = template.FormattedTypeNamespace;
+ if (ns is null)
+ {
+ formatterVisibility = "private";
+ return null;
+ }
+
+ int depth = 0;
+ foreach (string segment in ns.Split('.'))
+ {
+ string visibility = depth == 0 ? "private" : "internal";
+ writer($"{visibility} partial class {segment} {{ ");
+ depth++;
+ }
+
+ writer("\r\n");
+
+ formatterVisibility = "internal";
+ return new DisposeAction(() =>
+ {
+ for (int i = 0; i < depth; i++)
+ {
+ writer("}");
+ }
+ });
+ }
+
+ private class DisposeAction : IDisposable
+ {
+ private Action? disposeAction;
+
+ internal DisposeAction(Action? action)
+ {
+ this.disposeAction = action;
+ }
+
+ public void Dispose()
+ {
+ this.disposeAction?.Invoke();
+ this.disposeAction = null;
+ }
+ }
+}
diff --git a/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.cs b/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.cs
index dd14ede45..9814b62d9 100644
--- a/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.cs
+++ b/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.cs
@@ -2,7 +2,7 @@
//
// This code was generated by a tool.
// Runtime Version: 17.0.0.0
-//
+//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
@@ -13,7 +13,7 @@ namespace MessagePack.SourceGenerator.Transforms
using System.Text;
using System.Collections.Generic;
using System;
-
+
///
/// Class to produce the template output
///
@@ -25,9 +25,19 @@ public partial class UnionTemplate : UnionTemplateBase
///
public virtual string TransformText()
{
- this.Write("\r\nnamespace ");
- this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));
- this.Write("\r\n{\r\n\tusing MsgPack = global::MessagePack;\r\n\r\n\tinternal sealed class ");
+ this.Write("\r\n");
+ if (ResolverNamespace.Length > 0) {
+ this.Write("namespace ");
+ this.Write(this.ToStringHelper.ToStringWithCulture(ResolverNamespace));
+ this.Write(" {\r\n");
+ }
+ this.Write("\r\nusing MsgPack = global::MessagePack;\r\n\r\npartial class ");
+ this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
+ this.Write("\r\n{\r\n");
+ using (this.EmitClassesForNamespace(out string classVisibility, this.Write)) {
+ this.Write("\t");
+ this.Write(this.ToStringHelper.ToStringWithCulture(classVisibility));
+ this.Write(" sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.Name));
this.Write("Formatter : MsgPack::Formatters.IMessagePackFormatter<");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.FullName));
@@ -43,7 +53,7 @@ public virtual string TransformText()
"neric.KeyValuePair>(");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.SubTypes.Length));
this.Write(", MsgPack::Internal.RuntimeTypeHandleEqualityComparer.Default)\r\n\t\t\t{\r\n");
- for(var i = 0; i < Info.SubTypes.Length; i++) { var item = Info.SubTypes[i];
+ for(var i = 0; i < Info.SubTypes.Length; i++) { var item = Info.SubTypes[i];
this.Write("\t\t\t\t{ typeof(");
this.Write(this.ToStringHelper.ToStringWithCulture(item.Type));
this.Write(").TypeHandle, new global::System.Collections.Generic.KeyValuePair(");
@@ -51,18 +61,18 @@ public virtual string TransformText()
this.Write(", ");
this.Write(this.ToStringHelper.ToStringWithCulture(i));
this.Write(") },\r\n");
- }
+ }
this.Write("\t\t\t};\r\n\t\t\tthis.keyToJumpMap = new global::System.Collections.Generic.Dictionary(");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.SubTypes.Length));
this.Write(")\r\n\t\t\t{\r\n");
- for(var i = 0; i < Info.SubTypes.Length; i++) { var item = Info.SubTypes[i];
+ for(var i = 0; i < Info.SubTypes.Length; i++) { var item = Info.SubTypes[i];
this.Write("\t\t\t\t{ ");
this.Write(this.ToStringHelper.ToStringWithCulture(item.Key));
this.Write(", ");
this.Write(this.ToStringHelper.ToStringWithCulture(i));
this.Write(" },\r\n");
- }
+ }
this.Write("\t\t\t};\r\n\t\t}\r\n\r\n\t\tpublic void Serialize(ref MsgPack::MessagePackWriter writer, ");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.FullName));
this.Write(@" value, MsgPack::MessagePackSerializerOptions options)
@@ -75,7 +85,7 @@ public virtual string TransformText()
switch (keyValuePair.Value)
{
");
- for(var i = 0; i < Info.SubTypes.Length; i++) { var item = Info.SubTypes[i];
+ for(var i = 0; i < Info.SubTypes.Length; i++) { var item = Info.SubTypes[i];
this.Write("\t\t\t\t\tcase ");
this.Write(this.ToStringHelper.ToStringWithCulture(i));
this.Write(":\r\n\t\t\t\t\t\tMsgPack::FormatterResolverExtensions.GetFormatterWithVerify<");
@@ -83,7 +93,7 @@ public virtual string TransformText()
this.Write(">(options.Resolver).Serialize(ref writer, (");
this.Write(this.ToStringHelper.ToStringWithCulture(item.Type));
this.Write(")value, options);\r\n\t\t\t\t\t\tbreak;\r\n");
- }
+ }
this.Write("\t\t\t\t\tdefault:\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\twriter.WriteNil();\r" +
"\n\t\t}\r\n\r\n\t\tpublic ");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.FullName));
@@ -103,7 +113,7 @@ public virtual string TransformText()
"= -1;\r\n\t\t\t}\r\n\r\n\t\t\t");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.FullName));
this.Write(" result = null;\r\n\t\t\tswitch (key)\r\n\t\t\t{\r\n");
- for(var i = 0; i < Info.SubTypes.Length; i++) { var item = Info.SubTypes[i];
+ for(var i = 0; i < Info.SubTypes.Length; i++) { var item = Info.SubTypes[i];
this.Write("\t\t\t\tcase ");
this.Write(this.ToStringHelper.ToStringWithCulture(i));
this.Write(":\r\n\t\t\t\t\tresult = (");
@@ -111,9 +121,14 @@ public virtual string TransformText()
this.Write(")MsgPack::FormatterResolverExtensions.GetFormatterWithVerify<");
this.Write(this.ToStringHelper.ToStringWithCulture(item.Type));
this.Write(">(options.Resolver).Deserialize(ref reader, options);\r\n\t\t\t\t\tbreak;\r\n");
- }
+ }
this.Write("\t\t\t\tdefault:\r\n\t\t\t\t\treader.Skip();\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\treader.Depth--;\r\n\t\t\tre" +
- "turn result;\r\n\t\t}\r\n\t}\r\n}\r\n");
+ "turn result;\r\n\t\t}\r\n\t}\r\n");
+ } // close EmitClassesForNamespace
+ this.Write("}\r\n\r\n");
+ if (ResolverNamespace.Length > 0) {
+ this.Write("}\r\n");
+ }
return this.GenerationEnvironment.ToString();
}
}
@@ -216,7 +231,7 @@ public void Write(string textToAppend)
}
// If we're starting off, or if the previous text ended with a newline,
// we have to append the current indent first.
- if (((this.GenerationEnvironment.Length == 0)
+ if (((this.GenerationEnvironment.Length == 0)
|| this.endsWithNewline))
{
this.GenerationEnvironment.Append(this.currentIndentField);
diff --git a/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.tt b/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.tt
index 930c4046c..528f2e613 100644
--- a/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.tt
+++ b/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.tt
@@ -4,11 +4,16 @@
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
-namespace <#= Namespace #>
-{
- using MsgPack = global::MessagePack;
+<# if (ResolverNamespace.Length > 0) { #>
+namespace <#= ResolverNamespace #> {
+<# } #>
- internal sealed class <#= Info.Name #>Formatter : MsgPack::Formatters.IMessagePackFormatter<<#= Info.FullName #>>
+using MsgPack = global::MessagePack;
+
+partial class <#= ResolverName #>
+{
+<# using (this.EmitClassesForNamespace(out string classVisibility, this.Write)) { #>
+ <#= classVisibility #> sealed class <#= Info.Name #>Formatter : MsgPack::Formatters.IMessagePackFormatter<<#= Info.FullName #>>
{
private readonly global::System.Collections.Generic.Dictionary> typeToKeyAndJumpMap;
private readonly global::System.Collections.Generic.Dictionary keyToJumpMap;
@@ -90,4 +95,9 @@ namespace <#= Namespace #>
return result;
}
}
+<# } // close EmitClassesForNamespace #>
}
+
+<# if (ResolverNamespace.Length > 0) { #>
+}
+<# } #>
diff --git a/src/MessagePack.SourceGenerator/Usings.cs b/src/MessagePack.SourceGenerator/Usings.cs
new file mode 100644
index 000000000..8313513fc
--- /dev/null
+++ b/src/MessagePack.SourceGenerator/Usings.cs
@@ -0,0 +1,5 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+global using MessagePack.SourceGenerator.Analyzers;
+global using MessagePack.SourceGenerator.CodeAnalysis;
diff --git a/src/MessagePack.SourceGenerator/Utils/AnalyzerUtilities.cs b/src/MessagePack.SourceGenerator/Utils/AnalyzerUtilities.cs
new file mode 100644
index 000000000..795a02886
--- /dev/null
+++ b/src/MessagePack.SourceGenerator/Utils/AnalyzerUtilities.cs
@@ -0,0 +1,127 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using static MessagePack.SourceGenerator.Constants;
+
+namespace MessagePack.SourceGenerator;
+
+public static class AnalyzerUtilities
+{
+ ///
+ /// Keep this list in sync with DynamicObjectTypeBuilder.IsOptimizeTargetType.
+ ///
+ public static readonly string[] PrimitiveTypes =
+ {
+ "short",
+ "int",
+ "long",
+ "ushort",
+ "uint",
+ "ulong",
+ "float",
+ "double",
+ "bool",
+ "byte",
+ "sbyte",
+ "char",
+ "byte[]",
+
+ // Do not include types that resolvers are allowed to modify.
+ ////"global::System.DateTime", // OldSpec has no support, so for that and perf reasons a .NET native DateTime resolver exists.
+ ////"string", // https://github.com/Cysharp/MasterMemory provides custom formatter for string interning.
+ };
+
+ public static string GetFullNamespaceName(this INamespaceSymbol namespaceSymbol)
+ {
+ if (namespaceSymbol.IsGlobalNamespace)
+ {
+ return string.Empty;
+ }
+
+ string baseName = GetFullNamespaceName(namespaceSymbol.ContainingNamespace);
+ return string.IsNullOrEmpty(baseName) ? namespaceSymbol.Name : baseName + "." + namespaceSymbol.Name;
+ }
+
+ public static string GetCanonicalTypeFullName(this ITypeSymbol typeSymbol) => typeSymbol.WithNullableAnnotation(NullableAnnotation.None).ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+
+ internal static string GetHelpLink(string diagnosticId) => $"https://github.com/MessagePack-CSharp/MessagePack-CSharp/blob/master/doc/analyzers/{diagnosticId}.md";
+
+ internal static AnalyzerOptions? ParseGeneratorAttribute(ImmutableArray attributes, ISymbol targetSymbol, CancellationToken cancellationToken)
+ {
+ AttributeData? generatorAttribute = attributes.SingleOrDefault(ad =>
+ ad.AttributeClass?.Name == GeneratedMessagePackResolverAttributeName &&
+ ad.AttributeClass?.ContainingNamespace.Name == AttributeNamespace);
+
+ if (generatorAttribute is null)
+ {
+ return null;
+ }
+
+ FormattersOptions formattersOptions = new()
+ {
+ UsesMapMode = generatorAttribute.GetSingleNamedArgumentValue("UseMapMode") is true,
+ };
+
+ ResolverOptions resolverOptions = new()
+ {
+ Name = targetSymbol.Name,
+ Namespace = targetSymbol.ContainingNamespace.GetFullNamespaceName(),
+ };
+
+ GeneratorOptions generatorOptions = new()
+ {
+ Formatters = formattersOptions,
+ Resolver = resolverOptions,
+ };
+
+ AnalyzerOptions options = new()
+ {
+ Generator = generatorOptions,
+ IsGeneratingSource = true,
+ };
+
+ return options;
+ }
+
+ internal static ImmutableDictionary> ParseKnownFormatterAttribute(ImmutableArray attributes, CancellationToken cancellationToken)
+ {
+ var builder = ImmutableDictionary.CreateBuilder>();
+ foreach (AttributeData ad in attributes)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ if (ad.AttributeClass?.Name == MessagePackKnownFormatterAttributeName && ad.AttributeClass?.ContainingNamespace.Name == AttributeNamespace)
+ {
+ if (ad.ConstructorArguments[0].Value is INamedTypeSymbol formatter)
+ {
+ var formattableTypes = ImmutableHashSet.CreateBuilder();
+ foreach (INamedTypeSymbol iface in formatter.AllInterfaces)
+ {
+ if (iface is { IsGenericType: true, MetadataName: "IMessagePackFormatter`1", ContainingNamespace.Name: "Formatters", ContainingNamespace.ContainingNamespace.Name: "MessagePack" })
+ {
+ formattableTypes.Add(iface.TypeArguments[0].GetCanonicalTypeFullName());
+ }
+ }
+
+ if (formattableTypes.Count > 0)
+ {
+ builder.Add(formatter.GetCanonicalTypeFullName(), formattableTypes.ToImmutable());
+ }
+ }
+ }
+ }
+
+ return builder.ToImmutable();
+ }
+
+ internal static ImmutableArray ParseAssumedFormattableAttribute(ImmutableArray attributes, CancellationToken cancellationToken)
+ {
+ return ImmutableArray.CreateRange(
+ from ad in attributes
+ where ad.AttributeClass?.Name == MessagePackAssumedFormattableAttributeName && ad.AttributeClass?.ContainingNamespace.Name == AttributeNamespace
+ let type = (INamedTypeSymbol?)ad.ConstructorArguments[0].Value
+ where type is not null
+ select type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ }
+}
diff --git a/src/MessagePack.SourceGenerator/Utils/Constants.cs b/src/MessagePack.SourceGenerator/Utils/Constants.cs
new file mode 100644
index 000000000..b4fba4b25
--- /dev/null
+++ b/src/MessagePack.SourceGenerator/Utils/Constants.cs
@@ -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.
+
+namespace MessagePack.SourceGenerator;
+
+internal static class Constants
+{
+ internal const string AttributeNamespace = "MessagePack";
+ internal const string GeneratedMessagePackResolverAttributeName = "GeneratedMessagePackResolverAttribute";
+ internal const string MessagePackKnownFormatterAttributeName = "MessagePackKnownFormatterAttribute";
+ internal const string MessagePackAssumedFormattableAttributeName = "MessagePackAssumedFormattableAttribute";
+ internal const string MessagePackObjectAttributeName = "MessagePackObjectAttribute";
+ internal const string MessagePackUnionAttributeName = "UnionAttribute";
+}
diff --git a/src/MessagePack.Analyzers/Utils/RoslynAnalyzerExtensions.cs b/src/MessagePack.SourceGenerator/Utils/RoslynAnalyzerExtensions.cs
similarity index 98%
rename from src/MessagePack.Analyzers/Utils/RoslynAnalyzerExtensions.cs
rename to src/MessagePack.SourceGenerator/Utils/RoslynAnalyzerExtensions.cs
index 7ec0994c4..0f7e5ccf3 100644
--- a/src/MessagePack.Analyzers/Utils/RoslynAnalyzerExtensions.cs
+++ b/src/MessagePack.SourceGenerator/Utils/RoslynAnalyzerExtensions.cs
@@ -5,7 +5,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-namespace MessagePack.Analyzers;
+namespace MessagePack.SourceGenerator;
public static class RoslynAnalyzerExtensions
{
diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Annotations/AnalyzerAttributes.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Annotations/AnalyzerAttributes.cs
new file mode 100644
index 000000000..2b6f6099f
--- /dev/null
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Annotations/AnalyzerAttributes.cs
@@ -0,0 +1,58 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#pragma warning disable SA1649 // File name should match first type name
+#pragma warning disable SA1402 // File may only contain a single type
+
+using System;
+using System.Diagnostics;
+
+namespace MessagePack
+{
+ // TODO: allow these attributes to also appear on the partial resolver class too.
+ // This isn't an acceptable exclusive option because we want the analyzer to be able to run even when the resolver partial class isn't generated.
+
+ ///
+ /// Identifies a custom formatter (one that implements one or more IMessagePackFormatter<T> interfaces)
+ /// that should be considered when checking that types are serializable and/or included in a source-generated resolver.
+ ///
+ ///
+ /// Formatters identified with this attribute will be included in the source-generated resolver.
+ ///
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module, AllowMultiple = true)]
+ [Conditional("NEVERDEFINED")] // We only need this attribute for analysis, so we don't want it to be included in the user's built assembly.
+ public class MessagePackKnownFormatterAttribute : Attribute
+ {
+ public MessagePackKnownFormatterAttribute(Type formatterType)
+ {
+ this.FormatterType = formatterType;
+ }
+
+ ///
+ /// Gets a type that implements one or more IMessagePackFormatter<T> interfaces.
+ ///
+ public Type FormatterType { get; }
+ }
+
+ ///
+ /// Identifies a type for which a IMessagePackFormatter<T> exists and will be added manually to the IFormatterResolver by the program.
+ ///
+ ///
+ /// This attribute suppresses warnings by the MessagePack analyzer when it encounters references to the specified type within another serializable type.
+ /// When possible, using the is preferred.
+ ///
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module, AllowMultiple = true)]
+ [Conditional("NEVERDEFINED")] // We only need this attribute for analysis, so we don't want it to be included in the user's built assembly.
+ public class MessagePackAssumedFormattableAttribute : Attribute
+ {
+ public MessagePackAssumedFormattableAttribute(Type formattableType)
+ {
+ this.FormattableType = formattableType;
+ }
+
+ ///
+ /// Gets a type for which an IMessagePackFormatter<T> is known to exist and will be added via a custom resolver by the program at runtime.
+ ///
+ public Type FormattableType { get; }
+ }
+}
diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Annotations/AnalyzerAttributes.cs.meta b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Annotations/AnalyzerAttributes.cs.meta
new file mode 100644
index 000000000..3aa787fa8
--- /dev/null
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Annotations/AnalyzerAttributes.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 357caef6244245488732cb1675735ac7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/src/MessagePack/GeneratedMessagePackResolverAttribute.cs b/src/MessagePack/GeneratedMessagePackResolverAttribute.cs
new file mode 100644
index 000000000..430e6e66a
--- /dev/null
+++ b/src/MessagePack/GeneratedMessagePackResolverAttribute.cs
@@ -0,0 +1,30 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Diagnostics;
+
+namespace MessagePack
+{
+ ///
+ /// An attribute to apply to a that will serve as the
+ /// source-generated resolver for MessagePack.
+ ///
+ [AttributeUsage(System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+ [Conditional("NEVERDEFINED")] // We only need this attribute for source generation, so we don't want it to be included in the user's built assembly.
+ public class GeneratedMessagePackResolverAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GeneratedMessagePackResolverAttribute()
+ {
+ }
+
+ ///
+ /// Gets or sets a value indicating whether types will be serialized with their property names as well as their
+ /// values in a key=value dictionary, as opposed to an array of values.
+ ///
+ public bool UseMapMode { get; set; }
+ }
+}
diff --git a/src/MessagePack/net6.0/PublicAPI.Unshipped.txt b/src/MessagePack/net6.0/PublicAPI.Unshipped.txt
index 526d348e7..6018fd065 100644
--- a/src/MessagePack/net6.0/PublicAPI.Unshipped.txt
+++ b/src/MessagePack/net6.0/PublicAPI.Unshipped.txt
@@ -31,6 +31,10 @@ MessagePack.Formatters.Vector3Formatter.Serialize(ref MessagePack.MessagePackWri
MessagePack.Formatters.Vector4Formatter
MessagePack.Formatters.Vector4Formatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Numerics.Vector4
MessagePack.Formatters.Vector4Formatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Numerics.Vector4 value, MessagePack.MessagePackSerializerOptions! options) -> void
+MessagePack.GeneratedMessagePackResolverAttribute
+MessagePack.GeneratedMessagePackResolverAttribute.GeneratedMessagePackResolverAttribute() -> void
+MessagePack.GeneratedMessagePackResolverAttribute.UseMapMode.get -> bool
+MessagePack.GeneratedMessagePackResolverAttribute.UseMapMode.set -> void
MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int
MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions!
diff --git a/src/MessagePack/net8.0/PublicAPI.Unshipped.txt b/src/MessagePack/net8.0/PublicAPI.Unshipped.txt
index 49a951ce0..e994178e1 100644
--- a/src/MessagePack/net8.0/PublicAPI.Unshipped.txt
+++ b/src/MessagePack/net8.0/PublicAPI.Unshipped.txt
@@ -31,6 +31,10 @@ MessagePack.Formatters.Vector3Formatter.Serialize(ref MessagePack.MessagePackWri
MessagePack.Formatters.Vector4Formatter
MessagePack.Formatters.Vector4Formatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Numerics.Vector4
MessagePack.Formatters.Vector4Formatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Numerics.Vector4 value, MessagePack.MessagePackSerializerOptions! options) -> void
+MessagePack.GeneratedMessagePackResolverAttribute
+MessagePack.GeneratedMessagePackResolverAttribute.GeneratedMessagePackResolverAttribute() -> void
+MessagePack.GeneratedMessagePackResolverAttribute.UseMapMode.get -> bool
+MessagePack.GeneratedMessagePackResolverAttribute.UseMapMode.set -> void
MessagePack.ImmutableCollection.FrozenDictionaryFormatter
MessagePack.ImmutableCollection.FrozenDictionaryFormatter.FrozenDictionaryFormatter() -> void
MessagePack.ImmutableCollection.FrozenDictionaryFormatter.FrozenDictionaryFormatter(System.Collections.Generic.IEqualityComparer? comparer) -> void
diff --git a/src/MessagePack/netstandard2.0/PublicAPI.Unshipped.txt b/src/MessagePack/netstandard2.0/PublicAPI.Unshipped.txt
index bd780bd99..cbaff93f4 100644
--- a/src/MessagePack/netstandard2.0/PublicAPI.Unshipped.txt
+++ b/src/MessagePack/netstandard2.0/PublicAPI.Unshipped.txt
@@ -20,6 +20,10 @@ MessagePack.Formatters.Vector3Formatter.Serialize(ref MessagePack.MessagePackWri
MessagePack.Formatters.Vector4Formatter
MessagePack.Formatters.Vector4Formatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Numerics.Vector4
MessagePack.Formatters.Vector4Formatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Numerics.Vector4 value, MessagePack.MessagePackSerializerOptions! options) -> void
+MessagePack.GeneratedMessagePackResolverAttribute
+MessagePack.GeneratedMessagePackResolverAttribute.GeneratedMessagePackResolverAttribute() -> void
+MessagePack.GeneratedMessagePackResolverAttribute.UseMapMode.get -> bool
+MessagePack.GeneratedMessagePackResolverAttribute.UseMapMode.set -> void
MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int
MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions!
diff --git a/src/SourceGenerator.props b/src/SourceGenerator.props
index 9ba87f063..2b6874336 100644
--- a/src/SourceGenerator.props
+++ b/src/SourceGenerator.props
@@ -1,7 +1,7 @@
netstandard2.0
- 11
+ 12
enable
enable
cs
@@ -14,11 +14,4 @@
-
-
-
-
-
-
diff --git a/tests/MessagePack.Analyzers.Tests/MessagePack.Analyzers.Tests.csproj b/tests/MessagePack.Analyzers.Tests/MessagePack.Analyzers.Tests.csproj
deleted file mode 100644
index 787472b3d..000000000
--- a/tests/MessagePack.Analyzers.Tests/MessagePack.Analyzers.Tests.csproj
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
- net8.0
-
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/MessagePack.Analyzers/Usings.cs b/tests/MessagePack.SourceGenerator.ExecutionTests/GeneratedMessagePackResolver.cs
similarity index 65%
rename from src/MessagePack.Analyzers/Usings.cs
rename to tests/MessagePack.SourceGenerator.ExecutionTests/GeneratedMessagePackResolver.cs
index 4341e1664..45f5e6d6a 100644
--- a/src/MessagePack.Analyzers/Usings.cs
+++ b/tests/MessagePack.SourceGenerator.ExecutionTests/GeneratedMessagePackResolver.cs
@@ -1,4 +1,7 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-global using MessagePack.Analyzers.CodeAnalysis;
+[GeneratedMessagePackResolver]
+public partial class GeneratedMessagePackResolver
+{
+}
diff --git a/tests/MessagePack.SourceGenerator.ExecutionTests/MessagePack.SourceGenerator.ExecutionTests.csproj b/tests/MessagePack.SourceGenerator.ExecutionTests/MessagePack.SourceGenerator.ExecutionTests.csproj
index 3d033631b..952b99bea 100644
--- a/tests/MessagePack.SourceGenerator.ExecutionTests/MessagePack.SourceGenerator.ExecutionTests.csproj
+++ b/tests/MessagePack.SourceGenerator.ExecutionTests/MessagePack.SourceGenerator.ExecutionTests.csproj
@@ -5,13 +5,8 @@
net8.0
enable
enable
- true
-
-
-
-
diff --git a/tests/MessagePack.SourceGenerator.MapModeExecutionTests/GeneratedMessagePackResolver.cs b/tests/MessagePack.SourceGenerator.MapModeExecutionTests/GeneratedMessagePackResolver.cs
new file mode 100644
index 000000000..394e38675
--- /dev/null
+++ b/tests/MessagePack.SourceGenerator.MapModeExecutionTests/GeneratedMessagePackResolver.cs
@@ -0,0 +1,7 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+[GeneratedMessagePackResolver(UseMapMode = true)]
+internal partial class GeneratedMessagePackResolver
+{
+}
diff --git a/tests/MessagePack.SourceGenerator.MapModeExecutionTests/MessagePack.SourceGenerator.MapModeExecutionTests.csproj b/tests/MessagePack.SourceGenerator.MapModeExecutionTests/MessagePack.SourceGenerator.MapModeExecutionTests.csproj
index 77a4955a3..130721c04 100644
--- a/tests/MessagePack.SourceGenerator.MapModeExecutionTests/MessagePack.SourceGenerator.MapModeExecutionTests.csproj
+++ b/tests/MessagePack.SourceGenerator.MapModeExecutionTests/MessagePack.SourceGenerator.MapModeExecutionTests.csproj
@@ -1,19 +1,14 @@
-
+
net8.0
enable
enable
- true
-
-
-
-
-
+
diff --git a/tests/MessagePack.SourceGenerator.Tests/CodeAnalysis/FullModelTests.cs b/tests/MessagePack.SourceGenerator.Tests/CodeAnalysis/FullModelTests.cs
index 28c9ab79d..b3fec9771 100644
--- a/tests/MessagePack.SourceGenerator.Tests/CodeAnalysis/FullModelTests.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/CodeAnalysis/FullModelTests.cs
@@ -1,6 +1,8 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using Microsoft.CodeAnalysis;
+
public class FullModelTests
{
[Fact]
@@ -11,7 +13,7 @@ public void Equals_Null()
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
- AnalyzerOptions.Default);
+ new AnalyzerOptions());
Assert.False(model1.Equals(null));
}
@@ -25,20 +27,20 @@ public void Equals_ByValue()
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default).Add(new(null, "MyEnum", "My.MyEnum", "System.Int32")),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
- AnalyzerOptions.Default);
+ new AnalyzerOptions());
FullModel model1b = new(
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default).Add(new(null, "MyEnum", "My.MyEnum", "System.Int32")),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
- AnalyzerOptions.Default);
+ new AnalyzerOptions());
FullModel model2 = new(
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
ImmutableSortedSet.Create(ResolverRegisterInfoComparer.Default),
- AnalyzerOptions.Default);
+ new AnalyzerOptions());
Assert.Equal(model1b, model1a);
Assert.NotEqual(model2, model1a);
diff --git a/tests/MessagePack.SourceGenerator.Tests/CodeAnalysis/GenericSerializationInfoTests.cs b/tests/MessagePack.SourceGenerator.Tests/CodeAnalysis/GenericSerializationInfoTests.cs
index 97d8a09d6..714826a57 100644
--- a/tests/MessagePack.SourceGenerator.Tests/CodeAnalysis/GenericSerializationInfoTests.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/CodeAnalysis/GenericSerializationInfoTests.cs
@@ -8,9 +8,9 @@ public class GenericSerializationInfoTests
[Fact]
public void Equals_ByValue()
{
- GenericSerializationInfo info1a = new("full.name", "FullNameFormatter", false);
- GenericSerializationInfo info1b = new("full.name", "FullNameFormatter", false);
- GenericSerializationInfo info2 = new("full.Name", "FullNameFormatter", false);
+ GenericSerializationInfo info1a = new("full.name", "FullNameFormatter", null, false);
+ GenericSerializationInfo info1b = new("full.name", "FullNameFormatter", null, false);
+ GenericSerializationInfo info2 = new("full.Name", "FullNameFormatter", null, false);
Assert.Equal(info1b, info1a);
Assert.NotEqual(info2, info1a);
diff --git a/tests/MessagePack.SourceGenerator.Tests/GenerationTests.cs b/tests/MessagePack.SourceGenerator.Tests/GenerationTests.cs
index 3b720d896..6917caea2 100644
--- a/tests/MessagePack.SourceGenerator.Tests/GenerationTests.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/GenerationTests.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using MessagePack.SourceGenerator.Tests;
+using VerifyCS = CSharpSourceGeneratorVerifier;
public class GenerationTests
{
@@ -34,7 +35,165 @@ internal enum MyEnum
""";
testSource = TestUtilities.WrapTestSource(testSource, container);
- await VerifyCS.Test.RunDefaultAsync(testSource, options: AnalyzerOptions.Default with { Generator = GeneratorOptions.Default with { UsesMapMode = usesMapMode } }, testMethod: $"{nameof(EnumFormatter)}({container}, {usesMapMode})");
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource, options: new() { Generator = new() { Formatters = new() { UsesMapMode = usesMapMode } } }, testMethod: $"{nameof(EnumFormatter)}({container}, {usesMapMode})");
+ }
+
+ [Fact]
+ public async Task EnumFormatter_CollidingTypeNames()
+ {
+ string testSource = """
+using MessagePack;
+
+[MessagePackObject]
+internal class MyMessagePackObject
+{
+ [Key(0)]
+ internal NS1.MyEnum EnumValue1 { get; set; }
+
+ [Key(1)]
+ internal NS2.MyEnum EnumValue2 { get; set; }
+}
+
+namespace NS1 {
+ internal enum MyEnum
+ {
+ A, B, C
+ }
+}
+
+namespace NS2 {
+ internal enum MyEnum
+ {
+ D, E
+ }
+}
+""";
+
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
+ }
+
+ [Fact]
+ public async Task GenericType_CollidingTypeNames()
+ {
+ string testSource = """
+using MessagePack;
+
+[MessagePackObject]
+internal class MyMessagePackObject
+{
+ [Key(0)]
+ internal NS1.MyType Value1 { get; set; }
+
+ [Key(1)]
+ internal NS2.MyType Value2 { get; set; }
+}
+
+namespace NS1 {
+ [MessagePackObject]
+ internal class MyType
+ {
+ [Key(0)]
+ internal string Foo { get; set; }
+ }
+}
+
+namespace NS2 {
+ [MessagePackObject]
+ internal class MyType
+ {
+ [Key(0)]
+ internal string Foo { get; set; }
+ }
+}
+""";
+
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
+ }
+
+ [Fact]
+ public async Task NonGenericType_CollidingTypeNames()
+ {
+ string testSource = """
+using MessagePack;
+
+[MessagePackObject]
+internal class MyMessagePackObject
+{
+ [Key(0)]
+ internal NS1.MyType Value1 { get; set; }
+
+ [Key(1)]
+ internal NS2.MyType Value2 { get; set; }
+}
+
+namespace NS1 {
+ [MessagePackObject]
+ internal class MyType
+ {
+ [Key(0)]
+ internal string Foo { get; set; }
+ }
+}
+
+namespace NS2 {
+ [MessagePackObject]
+ internal class MyType
+ {
+ [Key(0)]
+ internal string Foo { get; set; }
+ }
+}
+""";
+
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
+ }
+
+ [Fact]
+ public async Task MixType_CollidingTypeNames()
+ {
+ string testSource = """
+using MessagePack;
+
+[MessagePackObject]
+internal class MyMessagePackObject
+{
+ [Key(0)]
+ internal NS1.MyType Value1 { get; set; }
+
+ [Key(1)]
+ internal NS2.MyType Value2 { get; set; }
+
+ [Key(2)]
+ internal NS3.MyType Value3 { get; set; }
+}
+
+namespace NS1 {
+ internal enum MyType
+ {
+ A, B
+ }
+}
+
+namespace NS2 {
+ [MessagePackObject]
+ internal class MyType
+ {
+ [Key(0)]
+ internal string Foo { get; set; }
+ }
+}
+
+namespace NS3 {
+ [MessagePackObject]
+ internal class MyType
+ {
+ [Key(0)]
+ internal string Foo { get; set; }
+ }
+}
+""";
+
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Theory, PairwiseData]
@@ -69,7 +228,7 @@ public UnserializableRecord Deserialize(ref MessagePackReader reader, MessagePac
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource, options: AnalyzerOptions.Default with { Generator = GeneratorOptions.Default with { UsesMapMode = usesMapMode } }, testMethod: $"{nameof(CustomFormatterViaAttributeOnProperty)}({usesMapMode})");
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource, options: new() { Generator = new() { Formatters = new() { UsesMapMode = usesMapMode } } }, testMethod: $"{nameof(CustomFormatterViaAttributeOnProperty)}({usesMapMode})");
}
[Theory, PairwiseData]
@@ -97,7 +256,7 @@ internal class MyMessagePackObject
""";
testSource = TestUtilities.WrapTestSource(testSource, container);
- await VerifyCS.Test.RunDefaultAsync(testSource, testMethod: $"{nameof(UnionFormatter)}({container})");
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource, testMethod: $"{nameof(UnionFormatter)}({container})");
}
[Fact]
@@ -118,7 +277,7 @@ internal class SubObject
{
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -142,7 +301,7 @@ internal class MyGenericType
internal T Value { get; set; }
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -162,7 +321,7 @@ public class GenericClass
public T2 MyProperty1 { get; set; }
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -178,6 +337,37 @@ class TypeWithAutoGeneratedFormatter
public MyCustomType Value { get; set; }
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource, options: AnalyzerOptions.Default with { CustomFormattedTypes = ImmutableHashSet.Empty.Add("MyCustomType") });
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource, options: new() { AssumedFormattableTypes = ImmutableHashSet.Empty.Add("MyCustomType") });
+ }
+
+ [Fact]
+ public async Task AdditionalFormatterTypes()
+ {
+ string testSource = Preamble + """
+[assembly: MessagePackKnownFormatterAttribute(typeof(MyCustomTypeFormatter))]
+
+class MyCustomType { }
+
+class MyCustomTypeFormatter : MessagePack.Formatters.IMessagePackFormatter
+{
+ public void Serialize(ref MessagePackWriter writer, MyCustomType value, MessagePackSerializerOptions options)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public MyCustomType Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
+ {
+ throw new System.NotImplementedException();
+ }
+}
+
+[MessagePackObject]
+class TypeWithAutoGeneratedFormatter
+{
+ [Key(0)]
+ public MyCustomType Value { get; set; }
+}
+""";
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
}
diff --git a/tests/MessagePack.SourceGenerator.Tests/GenericsFormatterTests.cs b/tests/MessagePack.SourceGenerator.Tests/GenericsFormatterTests.cs
index b77462779..2ce8d2707 100644
--- a/tests/MessagePack.SourceGenerator.Tests/GenericsFormatterTests.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/GenericsFormatterTests.cs
@@ -3,9 +3,17 @@
using MessagePack;
using Microsoft.CodeAnalysis;
+using VerifyCS = CSharpSourceGeneratorVerifier;
public class GenericsFormatterTests
{
+ private readonly ITestOutputHelper testOutputHelper;
+
+ public GenericsFormatterTests(ITestOutputHelper testOutputHelper)
+ {
+ this.testOutputHelper = testOutputHelper;
+ }
+
[Fact]
public async Task NullableFormatter()
{
@@ -33,7 +41,7 @@ public enum MyEnum
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -58,7 +66,7 @@ public class MyObject
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -82,7 +90,7 @@ public class Wrapper
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -120,7 +128,7 @@ public class MyInnerGenericObject
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -166,7 +174,7 @@ public class MyInnerGenericObject
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -198,7 +206,7 @@ public class MyGenericObject
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -233,7 +241,7 @@ public class MyObjectNested
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -270,7 +278,7 @@ public class MyObjectNested
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -293,7 +301,7 @@ public class MyGenericObject
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -315,7 +323,7 @@ public class MyGenericObject
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -346,7 +354,7 @@ public class MyGenericClass {}
public interface IMyInterface {}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -368,7 +376,7 @@ public class MyGenericObject
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -399,7 +407,7 @@ public class MyGenericObject
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -426,7 +434,7 @@ public class MyGenericObject
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
[Fact]
@@ -445,6 +453,9 @@ public class MyGenericObject
[Key(0)]
public T Content { get; set; }
}
+
+ [GeneratedMessagePackResolver]
+ partial class MyResolver { }
}
""";
diff --git a/tests/MessagePack.SourceGenerator.Tests/MessagePack.SourceGenerator.Tests.csproj b/tests/MessagePack.SourceGenerator.Tests/MessagePack.SourceGenerator.Tests.csproj
index 8ea134c1a..520e616a4 100644
--- a/tests/MessagePack.SourceGenerator.Tests/MessagePack.SourceGenerator.Tests.csproj
+++ b/tests/MessagePack.SourceGenerator.Tests/MessagePack.SourceGenerator.Tests.csproj
@@ -14,8 +14,11 @@
+
+
+
@@ -25,12 +28,8 @@
-
+
-
-
-
-
diff --git a/tests/MessagePack.Analyzers.Tests/MessagePackAnalyzerTests.cs b/tests/MessagePack.SourceGenerator.Tests/MessagePackAnalyzerTests.cs
similarity index 96%
rename from tests/MessagePack.Analyzers.Tests/MessagePackAnalyzerTests.cs
rename to tests/MessagePack.SourceGenerator.Tests/MessagePackAnalyzerTests.cs
index 29566dd72..248530f15 100644
--- a/tests/MessagePack.Analyzers.Tests/MessagePackAnalyzerTests.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/MessagePackAnalyzerTests.cs
@@ -1,12 +1,8 @@
// Copyright (c) All contributors. All rights reserved.
// 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;
+using VerifyCS = CSharpCodeFixVerifier;
public class MessagePackAnalyzerTests
{
diff --git a/tests/MessagePack.SourceGenerator.Tests/MessagePackFormatterAttributeTests.cs b/tests/MessagePack.SourceGenerator.Tests/MessagePackFormatterAttributeTests.cs
index 0836bf944..cdd88fad7 100644
--- a/tests/MessagePack.SourceGenerator.Tests/MessagePackFormatterAttributeTests.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/MessagePackFormatterAttributeTests.cs
@@ -1,8 +1,17 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using VerifyCS = CSharpSourceGeneratorVerifier;
+
public class MessagePackFormatterAttributeTests
{
+ private readonly ITestOutputHelper testOutputHelper;
+
+ public MessagePackFormatterAttributeTests(ITestOutputHelper testOutputHelper)
+ {
+ this.testOutputHelper = testOutputHelper;
+ }
+
[Fact]
public async Task CanGenerateMessagePackFormatterAttr()
{
@@ -41,6 +50,6 @@ public class Bar
}
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
}
diff --git a/tests/MessagePack.Analyzers.Tests/MsgPack001SpecifyOptionsAnalyzerTests.cs b/tests/MessagePack.SourceGenerator.Tests/MsgPack001SpecifyOptionsAnalyzerTests.cs
similarity index 82%
rename from tests/MessagePack.Analyzers.Tests/MsgPack001SpecifyOptionsAnalyzerTests.cs
rename to tests/MessagePack.SourceGenerator.Tests/MsgPack001SpecifyOptionsAnalyzerTests.cs
index a2a2a9ccd..0d42d3307 100644
--- a/tests/MessagePack.Analyzers.Tests/MsgPack001SpecifyOptionsAnalyzerTests.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/MsgPack001SpecifyOptionsAnalyzerTests.cs
@@ -6,8 +6,8 @@
using MessagePack.Analyzers;
using Microsoft.CodeAnalysis;
using Xunit;
-using VerifyCS = CSharpCodeFixVerifier;
-using VerifyVB = VisualBasicCodeFixVerifier;
+using VerifyCS = CSharpCodeFixVerifier;
+using VerifyVB = VisualBasicCodeFixVerifier;
public class MsgPack001SpecifyOptionsAnalyzerTests
{
diff --git a/tests/MessagePack.Analyzers.Tests/MsgPack002UseConstantOptionsAnalyzerTests.cs b/tests/MessagePack.SourceGenerator.Tests/MsgPack002UseConstantOptionsAnalyzerTests.cs
similarity index 91%
rename from tests/MessagePack.Analyzers.Tests/MsgPack002UseConstantOptionsAnalyzerTests.cs
rename to tests/MessagePack.SourceGenerator.Tests/MsgPack002UseConstantOptionsAnalyzerTests.cs
index 0ecab7b83..fd488794a 100644
--- a/tests/MessagePack.Analyzers.Tests/MsgPack002UseConstantOptionsAnalyzerTests.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/MsgPack002UseConstantOptionsAnalyzerTests.cs
@@ -6,8 +6,8 @@
using MessagePack.Analyzers;
using Microsoft.CodeAnalysis;
using Xunit;
-using VerifyCS = CSharpCodeFixVerifier;
-using VerifyVB = VisualBasicCodeFixVerifier;
+using VerifyCS = CSharpCodeFixVerifier;
+using VerifyVB = VisualBasicCodeFixVerifier;
public class MsgPack002UseConstantOptionsAnalyzerTests
{
diff --git a/tests/MessagePack.SourceGenerator.Tests/MultipleTypesTests.cs b/tests/MessagePack.SourceGenerator.Tests/MultipleTypesTests.cs
index 65de36f4d..f5569040d 100644
--- a/tests/MessagePack.SourceGenerator.Tests/MultipleTypesTests.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/MultipleTypesTests.cs
@@ -1,6 +1,8 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using VerifyCS = CSharpSourceGeneratorVerifier;
+
public class MultipleTypesTests
{
private readonly ITestOutputHelper testOutputHelper;
@@ -26,7 +28,7 @@ class Object2
{
}
""";
- await VerifyCS.Test.RunDefaultAsync(testSource, options: AnalyzerOptions.Default with { Generator = GeneratorOptions.Default with { UsesMapMode = usesMapMode } }, testMethod: $"{nameof(TwoTypes)}({usesMapMode})");
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource, options: new() { Generator = new() { Formatters = new() { UsesMapMode = usesMapMode } } }, testMethod: $"{nameof(TwoTypes)}({usesMapMode})");
}
[Fact]
@@ -35,6 +37,6 @@ public async Task ZeroTypes()
string testSource = """
using MessagePack;
""";
- await VerifyCS.Test.RunDefaultAsync(testSource);
+ await VerifyCS.Test.RunDefaultAsync(this.testOutputHelper, testSource);
}
}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/AddAttributeToType/Formatters.BarFormatter.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/AddAttributeToType/Formatters.BarFormatter.g.cs
deleted file mode 100644
index 62821ed0a..000000000
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/AddAttributeToType/Formatters.BarFormatter.g.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-
-#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649
-
-namespace Formatters
-{
- using MsgPack = global::MessagePack;
-
- internal sealed class BarFormatter : MsgPack::Formatters.IMessagePackFormatter
- {
-
- public void Serialize(ref MsgPack::MessagePackWriter writer, global::Bar value, MsgPack::MessagePackSerializerOptions options)
- {
- if (value == null)
- {
- writer.WriteNil();
- return;
- }
-
- MsgPack::IFormatterResolver formatterResolver = options.Resolver;
- writer.WriteArrayHeader(1);
- MsgPack::FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Serialize(ref writer, value.Member, options);
- }
-
- public global::Bar 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 ____result = new global::Bar();
-
- for (int i = 0; i < length; i++)
- {
- switch (i)
- {
- case 0:
- ____result.Member = MsgPack::FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Deserialize(ref reader, options);
- break;
- default:
- reader.Skip();
- break;
- }
- }
-
- reader.Depth--;
- return ____result;
- }
- }
-}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/AddAttributeToType/Formatters.FooFormatter.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/AddAttributeToType/Formatters.FooFormatter.g.cs
deleted file mode 100644
index 51d60277d..000000000
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/AddAttributeToType/Formatters.FooFormatter.g.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-
-#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649
-
-namespace Formatters
-{
- using MsgPack = global::MessagePack;
-
- internal sealed class FooFormatter : MsgPack::Formatters.IMessagePackFormatter
- {
-
- public void Serialize(ref MsgPack::MessagePackWriter writer, global::Foo value, MsgPack::MessagePackSerializerOptions options)
- {
- if (value == null)
- {
- writer.WriteNil();
- return;
- }
-
- writer.WriteArrayHeader(0);
- }
-
- public global::Foo Deserialize(ref MsgPack::MessagePackReader reader, MsgPack::MessagePackSerializerOptions options)
- {
- if (reader.TryReadNil())
- {
- return null;
- }
-
- reader.Skip();
- return new global::Foo();
- }
- }
-}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/AddAttributeToType/MessagePack.GeneratedMessagePackResolver.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/AddAttributeToType/MessagePack.GeneratedMessagePackResolver.g.cs
deleted file mode 100644
index e4b9452a9..000000000
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/AddAttributeToType/MessagePack.GeneratedMessagePackResolver.g.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-//
-
-#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649
-
-namespace MessagePack
-{
- using MsgPack = global::MessagePack;
- using Formatters = global::Formatters;
-
- /// A MessagePack resolver that uses generated formatters for types in this assembly.
- internal class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
- {
- /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();
-
- /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = MsgPack::Resolvers.CompositeResolver.Create(Instance, MsgPack::Resolvers.StandardAotResolver.Instance);
-
- private GeneratedMessagePackResolver()
- {
- }
-
- public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
- {
- return FormatterCache.Formatter;
- }
-
- private static class FormatterCache
- {
- internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
-
- static FormatterCache()
- {
- var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
- if (f != null)
- {
- Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
- }
- }
- }
- }
-
- internal static class GeneratedMessagePackResolverGetFormatterHelper
- {
- private static readonly global::System.Collections.Generic.Dictionary lookup;
-
- static GeneratedMessagePackResolverGetFormatterHelper()
- {
- lookup = new global::System.Collections.Generic.Dictionary(2)
- {
- { typeof(global::Bar), 0 },
- { typeof(global::Foo), 1 },
- };
- }
-
- internal static object GetFormatter(global::System.Type t)
- {
- int key;
- if (!lookup.TryGetValue(t, out key))
- {
- return null;
- }
-
- switch (key)
- {
- case 0: return new Formatters::BarFormatter();
- case 1: return new Formatters::FooFormatter();
- default: return null;
- }
- }
- }
-}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalAllowTypes/Formatters.TypeWithAutoGeneratedFormatterFormatter.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalAllowTypes/Formatters.TypeWithAutoGeneratedFormatterFormatter.g.cs
index 7e1403be6..dfcc11379 100644
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalAllowTypes/Formatters.TypeWithAutoGeneratedFormatterFormatter.g.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalAllowTypes/Formatters.TypeWithAutoGeneratedFormatterFormatter.g.cs
@@ -4,11 +4,13 @@
#pragma warning disable CS8669 // We may leak nullable annotations into generated code.
-namespace Formatters
-{
- using MsgPack = global::MessagePack;
+namespace MessagePack {
+
+using MsgPack = global::MessagePack;
- internal sealed class TypeWithAutoGeneratedFormatterFormatter : MsgPack::Formatters.IMessagePackFormatter
+partial class GeneratedMessagePackResolver
+{
+ private sealed class TypeWithAutoGeneratedFormatterFormatter : MsgPack::Formatters.IMessagePackFormatter
{
public void Serialize(ref MsgPack::MessagePackWriter writer, global::TypeWithAutoGeneratedFormatter value, MsgPack::MessagePackSerializerOptions options)
@@ -53,4 +55,7 @@ public void Serialize(ref MsgPack::MessagePackWriter writer, global::TypeWithAut
return ____result;
}
}
+
+}
+
}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalAllowTypes/MessagePack.GeneratedMessagePackResolver.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalAllowTypes/MessagePack.GeneratedMessagePackResolver.g.cs
index b5ca1194c..ff2080f1c 100644
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalAllowTypes/MessagePack.GeneratedMessagePackResolver.g.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalAllowTypes/MessagePack.GeneratedMessagePackResolver.g.cs
@@ -2,45 +2,43 @@
#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649
-namespace MessagePack
+namespace MessagePack {
+
+using MsgPack = global::MessagePack;
+
+/// A MessagePack resolver that uses generated formatters for types in this assembly.
+partial class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
{
- using MsgPack = global::MessagePack;
- using Formatters = global::Formatters;
+ /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();
- /// A MessagePack resolver that uses generated formatters for types in this assembly.
- internal class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
- {
- /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();
+ /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = new WithStandardAotResolver();
- /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = MsgPack::Resolvers.CompositeResolver.Create(Instance, MsgPack::Resolvers.StandardAotResolver.Instance);
+ private GeneratedMessagePackResolver()
+ {
+ }
- private GeneratedMessagePackResolver()
- {
- }
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
- public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
- {
- return FormatterCache.Formatter;
- }
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
- private static class FormatterCache
+ static FormatterCache()
{
- internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
-
- static FormatterCache()
+ var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
+ if (f != null)
{
- var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
- if (f != null)
- {
- Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
- }
+ Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
}
}
}
- internal static class GeneratedMessagePackResolverGetFormatterHelper
+ private static class GeneratedMessagePackResolverGetFormatterHelper
{
private static readonly global::System.Collections.Generic.Dictionary lookup;
@@ -48,8 +46,8 @@ static GeneratedMessagePackResolverGetFormatterHelper()
{
lookup = new global::System.Collections.Generic.Dictionary(1)
{
- { typeof(global::TypeWithAutoGeneratedFormatter), 0 },
- };
+ { typeof(global::TypeWithAutoGeneratedFormatter), 0 },
+ };
}
internal static object GetFormatter(global::System.Type t)
@@ -62,9 +60,24 @@ internal static object GetFormatter(global::System.Type t)
switch (key)
{
- case 0: return new Formatters::TypeWithAutoGeneratedFormatterFormatter();
- default: return null;
+ case 0: return new TypeWithAutoGeneratedFormatterFormatter();
+ default: return null;
}
}
}
+
+ private class WithStandardAotResolver : MsgPack::IFormatterResolver
+ {
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
+
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter = Instance.GetFormatter() ?? MsgPack::Resolvers.StandardAotResolver.Instance.GetFormatter();
+ }
+ }
+}
+
}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/Generics_Defined_In_ReferencedProject/TestProject.Formatters.TempProject.MyObjectFormatter.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalFormatterTypes/Formatters.TypeWithAutoGeneratedFormatterFormatter.g.cs
similarity index 58%
rename from tests/MessagePack.SourceGenerator.Tests/Resources/Generics_Defined_In_ReferencedProject/TestProject.Formatters.TempProject.MyObjectFormatter.g.cs
rename to tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalFormatterTypes/Formatters.TypeWithAutoGeneratedFormatterFormatter.g.cs
index 412999b1a..dfcc11379 100644
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/Generics_Defined_In_ReferencedProject/TestProject.Formatters.TempProject.MyObjectFormatter.g.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalFormatterTypes/Formatters.TypeWithAutoGeneratedFormatterFormatter.g.cs
@@ -4,14 +4,16 @@
#pragma warning disable CS8669 // We may leak nullable annotations into generated code.
-namespace Formatters.TempProject
-{
- using MsgPack = global::MessagePack;
+namespace MessagePack {
+
+using MsgPack = global::MessagePack;
- internal sealed class MyObjectFormatter : MsgPack::Formatters.IMessagePackFormatter
+partial class GeneratedMessagePackResolver
+{
+ private sealed class TypeWithAutoGeneratedFormatterFormatter : MsgPack::Formatters.IMessagePackFormatter
{
- public void Serialize(ref MsgPack::MessagePackWriter writer, global::TempProject.MyObject value, MsgPack::MessagePackSerializerOptions options)
+ public void Serialize(ref MsgPack::MessagePackWriter writer, global::TypeWithAutoGeneratedFormatter value, MsgPack::MessagePackSerializerOptions options)
{
if (value == null)
{
@@ -21,10 +23,10 @@ public void Serialize(ref MsgPack::MessagePackWriter writer, global::TempProject
MsgPack::IFormatterResolver formatterResolver = options.Resolver;
writer.WriteArrayHeader(1);
- MsgPack::FormatterResolverExtensions.GetFormatterWithVerify>(formatterResolver).Serialize(ref writer, value.Value, options);
+ MsgPack::FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Serialize(ref writer, value.Value, options);
}
- public global::TempProject.MyObject Deserialize(ref MsgPack::MessagePackReader reader, MsgPack::MessagePackSerializerOptions options)
+ public global::TypeWithAutoGeneratedFormatter Deserialize(ref MsgPack::MessagePackReader reader, MsgPack::MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
@@ -34,14 +36,14 @@ public void Serialize(ref MsgPack::MessagePackWriter writer, global::TempProject
options.Security.DepthStep(ref reader);
MsgPack::IFormatterResolver formatterResolver = options.Resolver;
var length = reader.ReadArrayHeader();
- var ____result = new global::TempProject.MyObject();
+ var ____result = new global::TypeWithAutoGeneratedFormatter();
for (int i = 0; i < length; i++)
{
switch (i)
{
case 0:
- ____result.Value = MsgPack::FormatterResolverExtensions.GetFormatterWithVerify>(formatterResolver).Deserialize(ref reader, options);
+ ____result.Value = MsgPack::FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Deserialize(ref reader, options);
break;
default:
reader.Skip();
@@ -53,4 +55,7 @@ public void Serialize(ref MsgPack::MessagePackWriter writer, global::TempProject
return ____result;
}
}
+
+}
+
}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalFormatterTypes/MessagePack.GeneratedMessagePackResolver.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalFormatterTypes/MessagePack.GeneratedMessagePackResolver.g.cs
new file mode 100644
index 000000000..ff2080f1c
--- /dev/null
+++ b/tests/MessagePack.SourceGenerator.Tests/Resources/AdditionalFormatterTypes/MessagePack.GeneratedMessagePackResolver.g.cs
@@ -0,0 +1,83 @@
+//
+
+#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649
+
+namespace MessagePack {
+
+using MsgPack = global::MessagePack;
+
+/// A MessagePack resolver that uses generated formatters for types in this assembly.
+partial class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
+{
+ /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();
+
+ /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = new WithStandardAotResolver();
+
+ private GeneratedMessagePackResolver()
+ {
+ }
+
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
+
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
+
+ static FormatterCache()
+ {
+ var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
+ if (f != null)
+ {
+ Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
+ }
+ }
+ }
+
+ private static class GeneratedMessagePackResolverGetFormatterHelper
+ {
+ private static readonly global::System.Collections.Generic.Dictionary lookup;
+
+ static GeneratedMessagePackResolverGetFormatterHelper()
+ {
+ lookup = new global::System.Collections.Generic.Dictionary(1)
+ {
+ { typeof(global::TypeWithAutoGeneratedFormatter), 0 },
+ };
+ }
+
+ internal static object GetFormatter(global::System.Type t)
+ {
+ int key;
+ if (!lookup.TryGetValue(t, out key))
+ {
+ return null;
+ }
+
+ switch (key)
+ {
+ case 0: return new TypeWithAutoGeneratedFormatterFormatter();
+ default: return null;
+ }
+ }
+ }
+
+ private class WithStandardAotResolver : MsgPack::IFormatterResolver
+ {
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
+
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter = Instance.GetFormatter() ?? MsgPack::Resolvers.StandardAotResolver.Instance.GetFormatter();
+ }
+ }
+}
+
+}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/Formatters.ContainerObjectFormatter.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/Formatters.ContainerObjectFormatter.g.cs
index ef37df632..f5cd5d75a 100644
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/Formatters.ContainerObjectFormatter.g.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/Formatters.ContainerObjectFormatter.g.cs
@@ -4,11 +4,13 @@
#pragma warning disable CS8669 // We may leak nullable annotations into generated code.
-namespace Formatters
-{
- using MsgPack = global::MessagePack;
+namespace MessagePack {
+
+using MsgPack = global::MessagePack;
- internal sealed class ContainerObjectFormatter : MsgPack::Formatters.IMessagePackFormatter
+partial class GeneratedMessagePackResolver
+{
+ private sealed class ContainerObjectFormatter : MsgPack::Formatters.IMessagePackFormatter
{
public void Serialize(ref MsgPack::MessagePackWriter writer, global::ContainerObject value, MsgPack::MessagePackSerializerOptions options)
@@ -53,4 +55,7 @@ public void Serialize(ref MsgPack::MessagePackWriter writer, global::ContainerOb
return ____result;
}
}
+
+}
+
}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/Formatters.SubObjectFormatter.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/Formatters.SubObjectFormatter.g.cs
index 4b05c039f..1be69fc97 100644
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/Formatters.SubObjectFormatter.g.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/Formatters.SubObjectFormatter.g.cs
@@ -4,11 +4,13 @@
#pragma warning disable CS8669 // We may leak nullable annotations into generated code.
-namespace Formatters
-{
- using MsgPack = global::MessagePack;
+namespace MessagePack {
+
+using MsgPack = global::MessagePack;
- internal sealed class SubObjectFormatter : MsgPack::Formatters.IMessagePackFormatter
+partial class GeneratedMessagePackResolver
+{
+ private sealed class SubObjectFormatter : MsgPack::Formatters.IMessagePackFormatter
{
public void Serialize(ref MsgPack::MessagePackWriter writer, global::SubObject value, MsgPack::MessagePackSerializerOptions options)
@@ -33,4 +35,7 @@ public void Serialize(ref MsgPack::MessagePackWriter writer, global::SubObject v
return new global::SubObject();
}
}
+
+}
+
}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/MessagePack.GeneratedMessagePackResolver.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/MessagePack.GeneratedMessagePackResolver.g.cs
index 6dba79ef5..3e0a189c2 100644
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/MessagePack.GeneratedMessagePackResolver.g.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/Resources/ArrayTypedProperty/MessagePack.GeneratedMessagePackResolver.g.cs
@@ -2,45 +2,43 @@
#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649
-namespace MessagePack
+namespace MessagePack {
+
+using MsgPack = global::MessagePack;
+
+/// A MessagePack resolver that uses generated formatters for types in this assembly.
+partial class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
{
- using MsgPack = global::MessagePack;
- using Formatters = global::Formatters;
+ /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();
- /// A MessagePack resolver that uses generated formatters for types in this assembly.
- internal class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
- {
- /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();
+ /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = new WithStandardAotResolver();
- /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = MsgPack::Resolvers.CompositeResolver.Create(Instance, MsgPack::Resolvers.StandardAotResolver.Instance);
+ private GeneratedMessagePackResolver()
+ {
+ }
- private GeneratedMessagePackResolver()
- {
- }
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
- public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
- {
- return FormatterCache.Formatter;
- }
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
- private static class FormatterCache
+ static FormatterCache()
{
- internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
-
- static FormatterCache()
+ var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
+ if (f != null)
{
- var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
- if (f != null)
- {
- Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
- }
+ Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
}
}
}
- internal static class GeneratedMessagePackResolverGetFormatterHelper
+ private static class GeneratedMessagePackResolverGetFormatterHelper
{
private static readonly global::System.Collections.Generic.Dictionary lookup;
@@ -48,10 +46,10 @@ static GeneratedMessagePackResolverGetFormatterHelper()
{
lookup = new global::System.Collections.Generic.Dictionary(3)
{
- { typeof(global::SubObject[]), 0 },
- { typeof(global::ContainerObject), 1 },
- { typeof(global::SubObject), 2 },
- };
+ { typeof(global::SubObject[]), 0 },
+ { typeof(global::ContainerObject), 1 },
+ { typeof(global::SubObject), 2 },
+ };
}
internal static object GetFormatter(global::System.Type t)
@@ -64,11 +62,26 @@ internal static object GetFormatter(global::System.Type t)
switch (key)
{
- case 0: return new MsgPack::Formatters.ArrayFormatter();
- case 1: return new Formatters::ContainerObjectFormatter();
- case 2: return new Formatters::SubObjectFormatter();
- default: return null;
+ case 0: return new MsgPack::Formatters.ArrayFormatter();
+ case 1: return new ContainerObjectFormatter();
+ case 2: return new SubObjectFormatter();
+ default: return null;
}
}
}
+
+ private class WithStandardAotResolver : MsgPack::IFormatterResolver
+ {
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
+
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter = Instance.GetFormatter() ?? MsgPack::Resolvers.StandardAotResolver.Instance.GetFormatter();
+ }
+ }
+}
+
}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/CanGenerateMessagePackFormatterAttr/Formatters.TempProject.BarFormatter.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/CanGenerateMessagePackFormatterAttr/Formatters.TempProject.BarFormatter.g.cs
index 82890b88f..7ed9ca4ec 100644
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/CanGenerateMessagePackFormatterAttr/Formatters.TempProject.BarFormatter.g.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/Resources/CanGenerateMessagePackFormatterAttr/Formatters.TempProject.BarFormatter.g.cs
@@ -4,10 +4,13 @@
#pragma warning disable CS8669 // We may leak nullable annotations into generated code.
-namespace Formatters.TempProject
-{
- using MsgPack = global::MessagePack;
+namespace MessagePack {
+
+using MsgPack = global::MessagePack;
+partial class GeneratedMessagePackResolver
+{
+private partial class TempProject {
internal sealed class BarFormatter : MsgPack::Formatters.IMessagePackFormatter
{
@@ -53,4 +56,7 @@ public void Serialize(ref MsgPack::MessagePackWriter writer, global::TempProject
return ____result;
}
}
+
+}}
+
}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/CanGenerateMessagePackFormatterAttr/MessagePack.GeneratedMessagePackResolver.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/CanGenerateMessagePackFormatterAttr/MessagePack.GeneratedMessagePackResolver.g.cs
index a1671a91c..cbf7fc876 100644
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/CanGenerateMessagePackFormatterAttr/MessagePack.GeneratedMessagePackResolver.g.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/Resources/CanGenerateMessagePackFormatterAttr/MessagePack.GeneratedMessagePackResolver.g.cs
@@ -2,45 +2,43 @@
#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649
-namespace MessagePack
+namespace MessagePack {
+
+using MsgPack = global::MessagePack;
+
+/// A MessagePack resolver that uses generated formatters for types in this assembly.
+partial class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
{
- using MsgPack = global::MessagePack;
- using Formatters = global::Formatters;
+ /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();
- /// A MessagePack resolver that uses generated formatters for types in this assembly.
- internal class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
- {
- /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();
+ /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = new WithStandardAotResolver();
- /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = MsgPack::Resolvers.CompositeResolver.Create(Instance, MsgPack::Resolvers.StandardAotResolver.Instance);
+ private GeneratedMessagePackResolver()
+ {
+ }
- private GeneratedMessagePackResolver()
- {
- }
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
- public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
- {
- return FormatterCache.Formatter;
- }
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
- private static class FormatterCache
+ static FormatterCache()
{
- internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
-
- static FormatterCache()
+ var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
+ if (f != null)
{
- var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
- if (f != null)
- {
- Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
- }
+ Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
}
}
}
- internal static class GeneratedMessagePackResolverGetFormatterHelper
+ private static class GeneratedMessagePackResolverGetFormatterHelper
{
private static readonly global::System.Collections.Generic.Dictionary lookup;
@@ -48,8 +46,8 @@ static GeneratedMessagePackResolverGetFormatterHelper()
{
lookup = new global::System.Collections.Generic.Dictionary(1)
{
- { typeof(global::TempProject.Bar), 0 },
- };
+ { typeof(global::TempProject.Bar), 0 },
+ };
}
internal static object GetFormatter(global::System.Type t)
@@ -62,9 +60,24 @@ internal static object GetFormatter(global::System.Type t)
switch (key)
{
- case 0: return new Formatters::TempProject.BarFormatter();
- default: return null;
+ case 0: return new TempProject.BarFormatter();
+ default: return null;
}
}
}
+
+ private class WithStandardAotResolver : MsgPack::IFormatterResolver
+ {
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
+
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter = Instance.GetFormatter() ?? MsgPack::Resolvers.StandardAotResolver.Instance.GetFormatter();
+ }
+ }
+}
+
}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/CustomFormatterViaAttributeOnProperty(False)/Formatters.HasPropertyWithCustomFormatterAttributeFormatter.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/CustomFormatterViaAttributeOnProperty(False)/Formatters.HasPropertyWithCustomFormatterAttributeFormatter.g.cs
index 2a4393b68..bda188bb5 100644
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/CustomFormatterViaAttributeOnProperty(False)/Formatters.HasPropertyWithCustomFormatterAttributeFormatter.g.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/Resources/CustomFormatterViaAttributeOnProperty(False)/Formatters.HasPropertyWithCustomFormatterAttributeFormatter.g.cs
@@ -4,11 +4,13 @@
#pragma warning disable CS8669 // We may leak nullable annotations into generated code.
-namespace Formatters
-{
- using MsgPack = global::MessagePack;
+namespace MessagePack {
+
+using MsgPack = global::MessagePack;
- internal sealed class HasPropertyWithCustomFormatterAttributeFormatter : MsgPack::Formatters.IMessagePackFormatter
+partial class GeneratedMessagePackResolver
+{
+ private sealed class HasPropertyWithCustomFormatterAttributeFormatter : MsgPack::Formatters.IMessagePackFormatter
{
private readonly global::UnserializableRecordFormatter __CustomValueCustomFormatter__ = new global::UnserializableRecordFormatter();
@@ -52,4 +54,7 @@ public void Serialize(ref MsgPack::MessagePackWriter writer, global::HasProperty
return ____result;
}
}
+
+}
+
}
diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/CustomFormatterViaAttributeOnProperty(False)/MessagePack.GeneratedMessagePackResolver.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/CustomFormatterViaAttributeOnProperty(False)/MessagePack.GeneratedMessagePackResolver.g.cs
index 4105a0db3..9595cc237 100644
--- a/tests/MessagePack.SourceGenerator.Tests/Resources/CustomFormatterViaAttributeOnProperty(False)/MessagePack.GeneratedMessagePackResolver.g.cs
+++ b/tests/MessagePack.SourceGenerator.Tests/Resources/CustomFormatterViaAttributeOnProperty(False)/MessagePack.GeneratedMessagePackResolver.g.cs
@@ -2,45 +2,43 @@
#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649
-namespace MessagePack
+namespace MessagePack {
+
+using MsgPack = global::MessagePack;
+
+/// A MessagePack resolver that uses generated formatters for types in this assembly.
+partial class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
{
- using MsgPack = global::MessagePack;
- using Formatters = global::Formatters;
+ /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();
- /// A MessagePack resolver that uses generated formatters for types in this assembly.
- internal class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
- {
- /// An instance of this resolver that only returns formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();
+ /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
+ public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = new WithStandardAotResolver();
- /// An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.
- public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = MsgPack::Resolvers.CompositeResolver.Create(Instance, MsgPack::Resolvers.StandardAotResolver.Instance);
+ private GeneratedMessagePackResolver()
+ {
+ }
- private GeneratedMessagePackResolver()
- {
- }
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
- public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
- {
- return FormatterCache.Formatter;
- }
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
- private static class FormatterCache
+ static FormatterCache()
{
- internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter;
-
- static FormatterCache()
+ var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
+ if (f != null)
{
- var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
- if (f != null)
- {
- Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
- }
+ Formatter = (MsgPack::Formatters.IMessagePackFormatter)f;
}
}
}
- internal static class GeneratedMessagePackResolverGetFormatterHelper
+ private static class GeneratedMessagePackResolverGetFormatterHelper
{
private static readonly global::System.Collections.Generic.Dictionary lookup;
@@ -48,8 +46,8 @@ static GeneratedMessagePackResolverGetFormatterHelper()
{
lookup = new global::System.Collections.Generic.Dictionary(1)
{
- { typeof(global::HasPropertyWithCustomFormatterAttribute), 0 },
- };
+ { typeof(global::HasPropertyWithCustomFormatterAttribute), 0 },
+ };
}
internal static object GetFormatter(global::System.Type t)
@@ -62,9 +60,24 @@ internal static object GetFormatter(global::System.Type t)
switch (key)
{
- case 0: return new Formatters::HasPropertyWithCustomFormatterAttributeFormatter();
- default: return null;
+ case 0: return new HasPropertyWithCustomFormatterAttributeFormatter();
+ default: return null;
}
}
}
+
+ private class WithStandardAotResolver : MsgPack::IFormatterResolver
+ {
+ public MsgPack::Formatters.IMessagePackFormatter GetFormatter()
+ {
+ return FormatterCache.Formatter;
+ }
+
+ private static class FormatterCache
+ {
+ internal static readonly MsgPack::Formatters.IMessagePackFormatter Formatter = Instance.GetFormatter