From e5b391c6a91f6d141f36ae09e31e2d6905262305 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Fri, 15 Nov 2024 14:54:30 -0700 Subject: [PATCH 1/3] Document `KeyAttribute` and `IgnoreMemberAttribute` migration steps --- docfx/docs/migrating.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docfx/docs/migrating.md b/docfx/docs/migrating.md index ac6b6e5e..5327faf6 100644 --- a/docfx/docs/migrating.md +++ b/docfx/docs/migrating.md @@ -73,8 +73,32 @@ Learn more about this in our [Getting Started](getting-started.md) guide. } ``` +#### `KeyAttribute` + Nerdbank.MessagePack also supports @Nerdbank.MessagePack.KeyAttribute, which serves the same function as in MessagePack-CSharp: to change the serialized schema from that of a map of property name=value to an array of values. -Thus, you may keep the `[Key(0)]`, `[Key(1)]`, etc., attributes on your types if you wish to maintain the schema of the serialized data. +Thus, you may keep the `[Key(0)]`, `[Key(1)]`, etc., attributes on your types if you wish to maintain the schema of the serialized data, provided you change the namespace. + +If using `[Key("name")]` attributes as a means to change the serialized property names, this must be replaced with @PolyType.PropertyShapeAttribute with @PolyType.PropertyShapeAttribute.Name?displayProperty=nameWithType set to the serialized name. + +```diff +-[Key("name")] ++[PropertyShape(Name = "name")] + public string SomeProperty { get; set; } +``` + +#### `IgnoreMemberAttribute` + +The `[IgnoreMemberAttribute]` that comes from MessagePack-CSharp can be removed from non-public members, which are never considered for serialization by default. +For public members that should be ignored, replace this attribute with @PolyType.PropertyShapeAttribute with @PolyType.PropertyShapeAttribute.Ignore?displayProperty=nameWithType set to `true`. + +```diff +-[IgnoreMember] ++[PropertyShape(Ignore = true)] + public int SomeProperty { get; set; } + +-[IgnoreMember] + internal int AnotherProperty { get; set; } +``` ### `UnionAttribute` From f03ca5b5dff57fd13c75f4a10475f5e2e869e16f Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Fri, 15 Nov 2024 16:01:23 -0700 Subject: [PATCH 2/3] Add NBMsgPack104 analyzer and code fix to replace use of `IgnoreMemberAttribute` --- docfx/analyzers/NBMsgPack104.md | 6 ++ docfx/analyzers/index.md | 1 + docfx/analyzers/toc.yml | 2 + .../MigrationCodeFix.cs | 58 ++++++++++++++++++- .../AnalyzerReleases.Unshipped.md | 1 + .../MessagePackCSharpReferenceSymbols.cs | 9 +++ .../MigrationAnalyzer.cs | 17 ++++++ .../Strings.resx | 6 ++ .../MigrationAnalyzerTests.cs | 12 ++++ 9 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 docfx/analyzers/NBMsgPack104.md diff --git a/docfx/analyzers/NBMsgPack104.md b/docfx/analyzers/NBMsgPack104.md new file mode 100644 index 00000000..601ff36b --- /dev/null +++ b/docfx/analyzers/NBMsgPack104.md @@ -0,0 +1,6 @@ +# NBMsgPack104: Remove use of IgnoreMemberAttribute + +This diagnostic is emitted where a `[MessagePack.IgnoreMemberAttribute]` attribute is used. +A code fix is offered to remove this attribute or switch to with @PolyType.PropertyShapeAttribute with its @PolyType.PropertyShapeAttribute.Ignore property set to `true`. + +[Learn more about migrating](../docs/migrating.md). diff --git a/docfx/analyzers/index.md b/docfx/analyzers/index.md index 726aaec5..0d60d97f 100644 --- a/docfx/analyzers/index.md +++ b/docfx/analyzers/index.md @@ -21,3 +21,4 @@ Rule ID | Category | Severity | Notes [NBMsgPack101](NBMsgPack101.md) | Migration | Info | Migrate to MessagePackConverterAttribute [NBMsgPack102](NBMsgPack102.md) | Migration | Info | Remove use of MessagePackObjectAttribute [NBMsgPack103](NBMsgPack103.md) | Migration | Info | Use newer KeyAttribute +[NBMsgPack104](NBMsgPack104.md) | Migration | Info | Remove use of IgnoreMemberAttribute diff --git a/docfx/analyzers/toc.yml b/docfx/analyzers/toc.yml index 68525b97..f5be3582 100644 --- a/docfx/analyzers/toc.yml +++ b/docfx/analyzers/toc.yml @@ -28,3 +28,5 @@ href: NBMsgPack102.md - name: NBMsgPack103 href: NBMsgPack103.md +- name: NBMsgPack104 + href: NBMsgPack104.md diff --git a/src/Nerdbank.MessagePack.Analyzers.CodeFixes/MigrationCodeFix.cs b/src/Nerdbank.MessagePack.Analyzers.CodeFixes/MigrationCodeFix.cs index 150b54bf..be5024df 100644 --- a/src/Nerdbank.MessagePack.Analyzers.CodeFixes/MigrationCodeFix.cs +++ b/src/Nerdbank.MessagePack.Analyzers.CodeFixes/MigrationCodeFix.cs @@ -26,11 +26,12 @@ public class MigrationCodeFix : CodeFixProvider MigrationAnalyzer.FormatterAttributeDiagnosticId, MigrationAnalyzer.MessagePackObjectAttributeUsageDiagnosticId, MigrationAnalyzer.KeyAttributeUsageDiagnosticId, + MigrationAnalyzer.IgnoreMemberAttributeUsageDiagnosticId, ]; public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; - public override Task RegisterCodeFixesAsync(CodeFixContext context) + public override async Task RegisterCodeFixesAsync(CodeFixContext context) { foreach (Diagnostic diagnostic in context.Diagnostics) { @@ -68,10 +69,34 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) equivalenceKey: "Use Nerdbank.MessagePack.KeyAttribute"), diagnostic); break; + case MigrationAnalyzer.IgnoreMemberAttributeUsageDiagnosticId: + // Determine whether to remove or replace the attribute and offer the appropriate code fix for it. + SyntaxNode? tree = await context.Document.GetSyntaxRootAsync(context.CancellationToken); + if (tree?.FindNode(context.Span) is AttributeSyntax { Parent.Parent: MemberDeclarationSyntax member } att) + { + if (member.Modifiers.Any(SyntaxKind.PublicKeyword)) + { + context.RegisterCodeFix( + CodeAction.Create( + title: "Replace IgnoreMemberAttribute with PropertyShapeAttribute", + createChangedDocument: cancellationToken => this.ReplaceIgnoreMemberAttribute(context.Document, att, false, cancellationToken), + equivalenceKey: "Replace IgnoreMemberAttribute with PropertyShapeAttribute"), + diagnostic); + } + else + { + context.RegisterCodeFix( + CodeAction.Create( + title: "Remove IgnoreMemberAttribute", + createChangedDocument: cancellationToken => this.ReplaceIgnoreMemberAttribute(context.Document, att, true, cancellationToken), + equivalenceKey: "Remove IgnoreMemberAttribute"), + diagnostic); + } + } + + break; } } - - return Task.CompletedTask; } private static NameSyntax NameInNamespace(SimpleNameSyntax name) => NameInNamespace(name, Namespace); @@ -237,11 +262,38 @@ private async Task ReplaceKeyAttributeAsync(Document document, TextSpa return document.WithSyntaxRoot(root); } + private async Task ReplaceIgnoreMemberAttribute(Document document, AttributeSyntax attribute, bool remove, CancellationToken cancellationToken) + { + CompilationUnitSyntax? root = (CompilationUnitSyntax)await attribute.SyntaxTree.GetRootAsync(cancellationToken); + + if (remove) + { + root = attribute.Parent is AttributeListSyntax { Attributes.Count: 1 } + ? root.RemoveNode(attribute.Parent, SyntaxRemoveOptions.KeepEndOfLine) + : root.RemoveNode(attribute, SyntaxRemoveOptions.KeepNoTrivia); + } + else + { + AttributeSyntax propertyShapeAttribute = Attribute(NameInNamespace(IdentifierName("PropertyShape"), IdentifierName("PolyType"))) + .AddArgumentListArguments( + AttributeArgument(LiteralExpression(SyntaxKind.TrueLiteralExpression)).WithNameEquals(NameEquals(IdentifierName("Ignore"))).WithAdditionalAnnotations(Formatter.Annotation)); + root = root.ReplaceNode(attribute, propertyShapeAttribute); + } + + if (root is null) + { + return document; + } + + return await this.AddImportAndSimplifyAsync(document.WithSyntaxRoot(root), cancellationToken); + } + private async Task AddImportAndSimplifyAsync(Document document, CancellationToken cancellationToken) { Document modifiedDocument = document; modifiedDocument = await ImportAdder.AddImportsAsync(document, cancellationToken: cancellationToken); modifiedDocument = await Simplifier.ReduceAsync(modifiedDocument, cancellationToken: cancellationToken); + modifiedDocument = await Formatter.FormatAsync(modifiedDocument, cancellationToken: cancellationToken); return modifiedDocument; } diff --git a/src/Nerdbank.MessagePack.Analyzers/AnalyzerReleases.Unshipped.md b/src/Nerdbank.MessagePack.Analyzers/AnalyzerReleases.Unshipped.md index 80c25590..07eac2f9 100644 --- a/src/Nerdbank.MessagePack.Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/Nerdbank.MessagePack.Analyzers/AnalyzerReleases.Unshipped.md @@ -20,3 +20,4 @@ NBMsgPack100 | Migration | Info | Migrate MessagePack-CSharp formatter NBMsgPack101 | Migration | Info | Migrate to MessagePackConverterAttribute NBMsgPack102 | Migration | Info | Remove use of MessagePackObjectAttribute NBMsgPack103 | Migration | Info | Use newer KeyAttribute +NBMsgPack104 | Migration | Info | Remove use of IgnoreMemberAttribute diff --git a/src/Nerdbank.MessagePack.Analyzers/MessagePackCSharpReferenceSymbols.cs b/src/Nerdbank.MessagePack.Analyzers/MessagePackCSharpReferenceSymbols.cs index cd23a006..40e38ac9 100644 --- a/src/Nerdbank.MessagePack.Analyzers/MessagePackCSharpReferenceSymbols.cs +++ b/src/Nerdbank.MessagePack.Analyzers/MessagePackCSharpReferenceSymbols.cs @@ -13,6 +13,7 @@ public record MessagePackCSharpReferenceSymbols( INamedTypeSymbol MessagePackFormatterAttribute, INamedTypeSymbol MessagePackObjectAttribute, INamedTypeSymbol KeyAttribute, + INamedTypeSymbol IgnoreMemberAttribute, INamedTypeSymbol MessagePackSecurity, IMethodSymbol DepthStep, INamedTypeSymbol MessagePackReader, @@ -89,6 +90,13 @@ public static bool TryCreate(Compilation compilation, [NotNullWhen(true)] out Me return false; } + INamedTypeSymbol? ignoreMemberAttribute = oldLibraryAnnotationsAssembly.GetTypeByMetadataName("MessagePack.IgnoreMemberAttribute"); + if (ignoreMemberAttribute is null) + { + referenceSymbols = null; + return false; + } + INamedTypeSymbol? messagePackSecurity = oldLibraryAssembly.GetTypeByMetadataName("MessagePack.MessagePackSecurity"); if (messagePackSecurity is null) { @@ -160,6 +168,7 @@ public static bool TryCreate(Compilation compilation, [NotNullWhen(true)] out Me messagePackFormatterAttribute, messagePackObjectAttribute, keyAttribute, + ignoreMemberAttribute, messagePackSecurity, depthStep, reader, diff --git a/src/Nerdbank.MessagePack.Analyzers/MigrationAnalyzer.cs b/src/Nerdbank.MessagePack.Analyzers/MigrationAnalyzer.cs index f831d3bb..9047f052 100644 --- a/src/Nerdbank.MessagePack.Analyzers/MigrationAnalyzer.cs +++ b/src/Nerdbank.MessagePack.Analyzers/MigrationAnalyzer.cs @@ -12,6 +12,7 @@ public class MigrationAnalyzer : DiagnosticAnalyzer public const string FormatterAttributeDiagnosticId = "NBMsgPack101"; public const string MessagePackObjectAttributeUsageDiagnosticId = "NBMsgPack102"; public const string KeyAttributeUsageDiagnosticId = "NBMsgPack103"; + public const string IgnoreMemberAttributeUsageDiagnosticId = "NBMsgPack104"; public static readonly DiagnosticDescriptor FormatterDiagnostic = new( id: FormatterDiagnosticId, @@ -49,11 +50,21 @@ public class MigrationAnalyzer : DiagnosticAnalyzer isEnabledByDefault: true, helpLinkUri: AnalyzerUtilities.GetHelpLink(KeyAttributeUsageDiagnosticId)); + public static readonly DiagnosticDescriptor IgnoreMemberAttributeUsageDiagnostic = new( + id: IgnoreMemberAttributeUsageDiagnosticId, + title: Strings.NBMsgPack104_Title, + messageFormat: Strings.NBMsgPack104_MessageFormat, + category: "Migration", + defaultSeverity: DiagnosticSeverity.Info, + isEnabledByDefault: true, + helpLinkUri: AnalyzerUtilities.GetHelpLink(IgnoreMemberAttributeUsageDiagnosticId)); + public override ImmutableArray SupportedDiagnostics => [ FormatterDiagnostic, FormatterAttributeDiagnostic, MessagePackObjectAttributeUsageDiagnostic, KeyAttributeUsageDiagnostic, + IgnoreMemberAttributeUsageDiagnostic, ]; public override void Initialize(AnalysisContext context) @@ -77,6 +88,12 @@ public override void Initialize(AnalysisContext context) { context.ReportDiagnostic(Diagnostic.Create(KeyAttributeUsageDiagnostic, keyAttribute.ApplicationSyntaxReference?.GetSyntax(context.CancellationToken).GetLocation())); } + + // Look for applications of [IgnoreMember] + if (context.Symbol.FindAttributes(oldLibrarySymbols.IgnoreMemberAttribute).FirstOrDefault() is AttributeData ignoreMemberAttribute) + { + context.ReportDiagnostic(Diagnostic.Create(IgnoreMemberAttributeUsageDiagnostic, ignoreMemberAttribute.ApplicationSyntaxReference?.GetSyntax(context.CancellationToken).GetLocation())); + } }, SymbolKind.Property, SymbolKind.Field); diff --git a/src/Nerdbank.MessagePack.Analyzers/Strings.resx b/src/Nerdbank.MessagePack.Analyzers/Strings.resx index 97e5f974..611cf526 100644 --- a/src/Nerdbank.MessagePack.Analyzers/Strings.resx +++ b/src/Nerdbank.MessagePack.Analyzers/Strings.resx @@ -213,4 +213,10 @@ Use newer KeyAttribute + + Use PolyType.PropertyShape(Ignore = true) instead. + + + Avoid IgnoreMemberAttribute + \ No newline at end of file diff --git a/test/Nerdbank.MessagePack.Analyzers.Tests/MigrationAnalyzerTests.cs b/test/Nerdbank.MessagePack.Analyzers.Tests/MigrationAnalyzerTests.cs index b0e33c66..2034f09a 100644 --- a/test/Nerdbank.MessagePack.Analyzers.Tests/MigrationAnalyzerTests.cs +++ b/test/Nerdbank.MessagePack.Analyzers.Tests/MigrationAnalyzerTests.cs @@ -142,17 +142,29 @@ public class MyType { [{|NBMsgPack103:Key(0)|}] public string Name { get; set; } + + [{|NBMsgPack104:IgnoreMember|}] + public string PublicIgnored { get; set; } + + [{|NBMsgPack104:IgnoreMember|}] + internal string NonPublicIgnored { get; set; } } """; string fixedSource = /* lang=c#-test */ """ using MessagePack; using MessagePack.Formatters; + using PolyType; public class MyType { [Nerdbank.MessagePack.Key(0)] public string Name { get; set; } + + [PropertyShape(Ignore = true)] + public string PublicIgnored { get; set; } + + internal string NonPublicIgnored { get; set; } } """; From 8346ecdd8fb188fd1cb5830fd9d3860cd0d674bf Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Fri, 15 Nov 2024 19:45:21 -0700 Subject: [PATCH 3/3] Enhance NBMsgPack103 code fix to support `[Key("name")]` --- .../MigrationCodeFix.cs | 45 +++++++++++++---- .../MigrationAnalyzerTests.cs | 49 ++++++++++++++++--- 2 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/Nerdbank.MessagePack.Analyzers.CodeFixes/MigrationCodeFix.cs b/src/Nerdbank.MessagePack.Analyzers.CodeFixes/MigrationCodeFix.cs index be5024df..12cc0c0f 100644 --- a/src/Nerdbank.MessagePack.Analyzers.CodeFixes/MigrationCodeFix.cs +++ b/src/Nerdbank.MessagePack.Analyzers.CodeFixes/MigrationCodeFix.cs @@ -62,16 +62,31 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) diagnostic); break; case MigrationAnalyzer.KeyAttributeUsageDiagnosticId: - context.RegisterCodeFix( - CodeAction.Create( - title: "Use Nerdbank.MessagePack.KeyAttribute", - createChangedDocument: cancellationToken => this.ReplaceKeyAttributeAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), - equivalenceKey: "Use Nerdbank.MessagePack.KeyAttribute"), - diagnostic); + // Determine whether to replace with KeyAttribute or PropertyShapeAttribute. + SyntaxNode? tree = await context.Document.GetSyntaxRootAsync(context.CancellationToken); + if (tree?.FindNode(context.Span) is AttributeSyntax keyAttribute && IsStringKeyAttribute(keyAttribute)) + { + context.RegisterCodeFix( + CodeAction.Create( + title: "Use PropertyShape(Name) instead", + createChangedDocument: cancellationToken => this.ReplaceKeyAttributeAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), + equivalenceKey: "Use PropertyShapeAttribute.Name"), + diagnostic); + } + else + { + context.RegisterCodeFix( + CodeAction.Create( + title: "Use Nerdbank.MessagePack.KeyAttribute", + createChangedDocument: cancellationToken => this.ReplaceKeyAttributeAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), + equivalenceKey: "Use Nerdbank.MessagePack.KeyAttribute"), + diagnostic); + } + break; case MigrationAnalyzer.IgnoreMemberAttributeUsageDiagnosticId: // Determine whether to remove or replace the attribute and offer the appropriate code fix for it. - SyntaxNode? tree = await context.Document.GetSyntaxRootAsync(context.CancellationToken); + tree = await context.Document.GetSyntaxRootAsync(context.CancellationToken); if (tree?.FindNode(context.Span) is AttributeSyntax { Parent.Parent: MemberDeclarationSyntax member } att) { if (member.Modifiers.Any(SyntaxKind.PublicKeyword)) @@ -99,6 +114,11 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) } } + private static bool IsStringKeyAttribute(AttributeSyntax attribute) + { + return attribute is { ArgumentList.Arguments: [AttributeArgumentSyntax { Expression: LiteralExpressionSyntax { RawKind: (int)SyntaxKind.StringLiteralExpression } }] }; + } + private static NameSyntax NameInNamespace(SimpleNameSyntax name) => NameInNamespace(name, Namespace); private static NameSyntax NameInNamespace(SimpleNameSyntax name, NameSyntax @namespace) => QualifiedName(@namespace, name).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation, Simplifier.Annotation); @@ -256,10 +276,17 @@ private async Task ReplaceKeyAttributeAsync(Document document, TextSpa return document; } - AttributeSyntax newAttribute = attribute.WithName(NameInNamespace(IdentifierName("Key"))); + // One of: + // => PropertyShape(Name = "") + // => [Key(n)] + AttributeSyntax newAttribute = IsStringKeyAttribute(attribute) + ? Attribute(NameInNamespace(IdentifierName("PropertyShape"), IdentifierName("PolyType"))) + .AddArgumentListArguments(attribute.ArgumentList!.Arguments[0].WithNameEquals(NameEquals(IdentifierName("Name"))).WithAdditionalAnnotations(Formatter.Annotation)) + : attribute.WithName(NameInNamespace(IdentifierName("Key"))); + root = root.ReplaceNode(attribute, newAttribute); - return document.WithSyntaxRoot(root); + return await this.AddImportAndSimplifyAsync(document.WithSyntaxRoot(root), cancellationToken); } private async Task ReplaceIgnoreMemberAttribute(Document document, AttributeSyntax attribute, bool remove, CancellationToken cancellationToken) diff --git a/test/Nerdbank.MessagePack.Analyzers.Tests/MigrationAnalyzerTests.cs b/test/Nerdbank.MessagePack.Analyzers.Tests/MigrationAnalyzerTests.cs index 2034f09a..696d810e 100644 --- a/test/Nerdbank.MessagePack.Analyzers.Tests/MigrationAnalyzerTests.cs +++ b/test/Nerdbank.MessagePack.Analyzers.Tests/MigrationAnalyzerTests.cs @@ -130,7 +130,7 @@ public override void Write(ref Nerdbank.MessagePack.MessagePackWriter writer, in await this.VerifyCodeFixAsync(source, fixedSource); } - [Fact(Skip = "Not yet passing due to unexpected iteration count")] + [Fact] public async Task MessagePackObject_Keys() { string source = /* lang=c#-test */ """ @@ -142,10 +142,37 @@ public class MyType { [{|NBMsgPack103:Key(0)|}] public string Name { get; set; } + } + """; + + string fixedSource = /* lang=c#-test */ """ + using MessagePack; + using MessagePack.Formatters; + + public class MyType + { + [Nerdbank.MessagePack.Key(0)] + public string Name { get; set; } + } + """; + + await this.VerifyCodeFixAsync(source, fixedSource, 2); + } + + [Fact] + public async Task MessagePackObject_IgnoreMember() + { + string source = /* lang=c#-test */ """ + using MessagePack; + using MessagePack.Formatters; + + public class MyType + { + public string Name { get; set; } [{|NBMsgPack104:IgnoreMember|}] public string PublicIgnored { get; set; } - + [{|NBMsgPack104:IgnoreMember|}] internal string NonPublicIgnored { get; set; } } @@ -158,7 +185,6 @@ public class MyType public class MyType { - [Nerdbank.MessagePack.Key(0)] public string Name { get; set; } [PropertyShape(Ignore = true)] @@ -168,7 +194,7 @@ public class MyType } """; - await this.VerifyCodeFixAsync(source, fixedSource); + await this.VerifyCodeFixAsync(source, fixedSource, 2); } [Fact] @@ -182,20 +208,27 @@ public async Task MessagePackObject_Map() public class MyType { public string Name { get; set; } + + [{|NBMsgPack103:Key("AnotherName")|}] + public string AnotherProperty { get; set; } } """; string fixedSource = /* lang=c#-test */ """ using MessagePack; using MessagePack.Formatters; + using PolyType; public class MyType { public string Name { get; set; } + + [PropertyShape(Name = "AnotherName")] + public string AnotherProperty { get; set; } } """; - await this.VerifyCodeFixAsync(source, fixedSource); + await this.VerifyCodeFixAsync(source, fixedSource, 2); } /// @@ -247,7 +280,7 @@ void Foo() await this.VerifyCodeFixAsync(source, fixedSource); } - private Task VerifyCodeFixAsync([StringSyntax("c#-test")] string source, [StringSyntax("c#-test")] string fixedSource) + private Task VerifyCodeFixAsync([StringSyntax("c#-test")] string source, [StringSyntax("c#-test")] string fixedSource, int iterations = 1) { return new VerifyCS.Test { @@ -256,6 +289,10 @@ private Task VerifyCodeFixAsync([StringSyntax("c#-test")] string source, [String ReferenceAssemblies = ReferencesHelper.DefaultTargetFrameworkReferences.WithPackages([ new PackageIdentity("MessagePack", "2.5.187"), ]), + NumberOfFixAllInDocumentIterations = iterations, + NumberOfFixAllInProjectIterations = iterations, + NumberOfFixAllIterations = iterations, + NumberOfIncrementalIterations = iterations, }.RunAsync(); } }