From e1122c4cdd743724ca81014decd8dfec8513b3fa Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Fri, 4 Aug 2023 12:35:28 +0200 Subject: [PATCH 01/15] Set next BenchmarkDotNet version: 0.13.8 --- README.md | 2 +- build/common.props | 2 +- build/versions.txt | 3 ++- .../.template.config/template.json | 2 +- .../.template.config/template.json | 2 +- .../.template.config/template.json | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4d3518d5bf..df84b5d462 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ It's no harder than writing unit tests! Under the hood, it performs a lot of [magic](#automation) that guarantees [reliable and precise](#reliability) results thanks to the [perfolizer](https://github.com/AndreyAkinshin/perfolizer) statistical engine. BenchmarkDotNet protects you from popular benchmarking mistakes and warns you if something is wrong with your benchmark design or obtained measurements. The results are presented in a [user-friendly](#friendliness) form that highlights all the important facts about your experiment. -BenchmarkDotNet is already adopted by [16600+ GitHub projects](https://github.com/dotnet/BenchmarkDotNet/network/dependents) including +BenchmarkDotNet is already adopted by [16900+ GitHub projects](https://github.com/dotnet/BenchmarkDotNet/network/dependents) including [.NET Runtime](https://github.com/dotnet/runtime), [.NET Compiler](https://github.com/dotnet/roslyn), [.NET Performance](https://github.com/dotnet/performance), diff --git a/build/common.props b/build/common.props index be1a13bffc..f84648040b 100644 --- a/build/common.props +++ b/build/common.props @@ -39,7 +39,7 @@ - 0.13.7 + 0.13.8 diff --git a/build/versions.txt b/build/versions.txt index 12a1f57f12..1f6634ffbc 100644 --- a/build/versions.txt +++ b/build/versions.txt @@ -51,4 +51,5 @@ 0.13.4 0.13.5 0.13.6 -0.13.7 \ No newline at end of file +0.13.7 +0.13.8 \ No newline at end of file diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json index b4051ccbf4..567259b34d 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json @@ -144,7 +144,7 @@ "type": "parameter", "datatype": "string", "description": "Version of BenchmarkDotNet that will be referenced.", - "defaultValue": "0.13.7", + "defaultValue": "0.13.8", "replaces": "$(BenchmarkDotNetVersion)" } }, diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json index 20d49ec024..12f8c5b9bd 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json @@ -144,7 +144,7 @@ "type": "parameter", "datatype": "string", "description": "Version of BenchmarkDotNet that will be referenced.", - "defaultValue": "0.13.7", + "defaultValue": "0.13.8", "replaces": "$(BenchmarkDotNetVersion)" } }, diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json index 1fe705443d..0e8597a653 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json @@ -144,7 +144,7 @@ "type": "parameter", "datatype": "string", "description": "Version of BenchmarkDotNet that will be referenced.", - "defaultValue": "0.13.7", + "defaultValue": "0.13.8", "replaces": "$(BenchmarkDotNetVersion)" } }, From b2235af5a4003bfd2d0ff659acf13fcaa0da3cbc Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Fri, 4 Aug 2023 13:05:27 +0200 Subject: [PATCH 02/15] [build] Remove explicit snupkg pushing *.snupkg files are pushed together with corresponding *.nupkg --- .github/workflows/publish-nightly.yaml | 4 ---- build/BenchmarkDotNet.Build/Runners/ReleaseRunner.cs | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/publish-nightly.yaml b/.github/workflows/publish-nightly.yaml index f062468a5d..3a3dc0bc9b 100644 --- a/.github/workflows/publish-nightly.yaml +++ b/.github/workflows/publish-nightly.yaml @@ -25,7 +25,3 @@ jobs: env: MYGET_API_KEY: ${{ secrets.MYGET_API_KEY }} run: ./.dotnet/dotnet nuget push **/*.nupkg --source https://www.myget.org/F/benchmarkdotnet/api/v3/index.json --api-key $MYGET_API_KEY --timeout 600 - - name: Publish snupkg - env: - MYGET_API_KEY: ${{ secrets.MYGET_API_KEY }} - run: ./.dotnet/dotnet nuget push **/*.snupkg --source https://www.myget.org/F/benchmarkdotnet/api/v3/index.json --api-key $MYGET_API_KEY --timeout 600 diff --git a/build/BenchmarkDotNet.Build/Runners/ReleaseRunner.cs b/build/BenchmarkDotNet.Build/Runners/ReleaseRunner.cs index b1d2e191e4..0562639a9b 100644 --- a/build/BenchmarkDotNet.Build/Runners/ReleaseRunner.cs +++ b/build/BenchmarkDotNet.Build/Runners/ReleaseRunner.cs @@ -101,7 +101,7 @@ private void PushNupkg() var nuGetToken = EnvVar.NuGetToken.GetValue(); var files = context - .GetFiles(context.ArtifactsDirectory.CombineWithFilePath("*").FullPath) + .GetFiles(context.ArtifactsDirectory.CombineWithFilePath("*.nupkg").FullPath) .OrderBy(file => file.FullPath); var settings = new DotNetNuGetPushSettings { From 2c999b9a2396a2c8138fa6e5ec093c6f35326b6a Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Fri, 4 Aug 2023 13:06:57 +0200 Subject: [PATCH 03/15] [build] Fix ReleaseTask console help info --- build/BenchmarkDotNet.Build/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/BenchmarkDotNet.Build/Program.cs b/build/BenchmarkDotNet.Build/Program.cs index 1ff1a26a7b..ce291ecd0f 100644 --- a/build/BenchmarkDotNet.Build/Program.cs +++ b/build/BenchmarkDotNet.Build/Program.cs @@ -222,7 +222,7 @@ public class ReleaseTask : FrostingTask, IHelpProvider { new Example(Name) .WithArgument(KnownOptions.Stable) - .WithArgument(KnownOptions.NextVersion, "v0.1.1729") + .WithArgument(KnownOptions.NextVersion, "0.1.1729") .WithArgument(KnownOptions.Push) } }; From d058c7b6ff2434e96f177f976a0ce06e1a1643b7 Mon Sep 17 00:00:00 2001 From: Benny Tordrup Date: Thu, 10 Aug 2023 11:40:23 +0200 Subject: [PATCH 04/15] Issue2394 multiple markdown exporters not possible even with different names (#2395) * Merge exporters by exporter.Name instead of exporter.GetType() * Added test that MarkdownExporter.GitHub and MarkdownExporter.Atlassian are both accepted in the merged configuration --- .../Configs/ImmutableConfigBuilder.cs | 28 +++++++++---------- .../Configs/ImmutableConfigTests.cs | 15 +++++++++- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs index f5d857df1d..0540dd1426 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs @@ -109,28 +109,28 @@ void AddWarning(string message) configAnalyse.Add(conclusion); } - var mergeDictionary = new Dictionary(); + var mergeDictionary = new Dictionary(); foreach (var exporter in exporters) { - var exporterType = exporter.GetType(); - if (mergeDictionary.ContainsKey(exporterType)) + var exporterName = exporter.Name; + if (mergeDictionary.ContainsKey(exporterName)) { - AddWarning($"The exporter {exporterType} is already present in configuration. There may be unexpected results."); + AddWarning($"The exporter {exporterName} is already present in configuration. There may be unexpected results."); } - mergeDictionary[exporterType] = exporter; + mergeDictionary[exporterName] = exporter; } foreach (var diagnoser in uniqueDiagnosers) foreach (var exporter in diagnoser.Exporters) { - var exporterType = exporter.GetType(); - if (mergeDictionary.ContainsKey(exporterType)) + var exporterName = exporter.Name; + if (mergeDictionary.ContainsKey(exporterName)) { - AddWarning($"The exporter {exporterType} of {diagnoser.GetType().Name} is already present in configuration. There may be unexpected results."); + AddWarning($"The exporter {exporterName} of {diagnoser.GetType().Name} is already present in configuration. There may be unexpected results."); } - mergeDictionary[exporterType] = exporter; + mergeDictionary[exporterName] = exporter; } var result = mergeDictionary.Values.ToList(); @@ -143,7 +143,7 @@ void AddWarning(string message) if (hardwareCounterDiagnoser != default(IHardwareCountersDiagnoser) && disassemblyDiagnoser != default(DisassemblyDiagnoser)) result.Add(new InstructionPointerExporter(hardwareCounterDiagnoser, disassemblyDiagnoser)); - for (int i = result.Count - 1; i >=0; i--) + for (int i = result.Count - 1; i >= 0; i--) if (result[i] is IExporterDependencies exporterDependencies) foreach (var dependency in exporterDependencies.Dependencies) /* @@ -165,7 +165,7 @@ void AddWarning(string message) * "The CsvMeasurementsExporter is already present in the configuration. There may be unexpected results of RPlotExporter. * */ - if (!result.Any(exporter=> exporter.GetType() == dependency.GetType())) + if (!result.Any(exporter => exporter.GetType() == dependency.GetType())) result.Insert(i, dependency); // All the exporter dependencies should be added before the exporter else { @@ -186,9 +186,9 @@ private static ImmutableHashSet GetAnalysers(IEnumerable a builder.Add(analyser); foreach (var diagnoser in uniqueDiagnosers) - foreach (var analyser in diagnoser.Analysers) - if (!builder.Contains(analyser)) - builder.Add(analyser); + foreach (var analyser in diagnoser.Analysers) + if (!builder.Contains(analyser)) + builder.Add(analyser); return builder.ToImmutable(); } diff --git a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs index 82c01d3b81..2eae58c7c1 100644 --- a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs +++ b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs @@ -143,6 +143,19 @@ public void DuplicateExportersAreExcluded() Assert.Same(MarkdownExporter.GitHub, final.GetExporters().Single()); } + [Fact] + public void MultipleExportersOfSameTypeWithDifferentNamesAreAccepted() + { + var mutable = ManualConfig.CreateEmpty(); + + mutable.AddExporter(MarkdownExporter.GitHub); + mutable.AddExporter(MarkdownExporter.Atlassian); + + var final = ImmutableConfigBuilder.Create(mutable); + + Assert.Equal(2, final.GetExporters().Count()); + } + [Fact] public void DuplicateAnalyzersAreExcluded() { @@ -380,7 +393,7 @@ private static ImmutableConfig[] AddLeftToTheRightAndRightToTheLef(ManualConfig var leftAddedToTheRight = ManualConfig.Create(right); leftAddedToTheRight.Add(left); - return new[]{ rightAddedToLeft.CreateImmutableConfig(), leftAddedToTheRight.CreateImmutableConfig() }; + return new[] { rightAddedToLeft.CreateImmutableConfig(), leftAddedToTheRight.CreateImmutableConfig() }; } public class TestExporter : IExporter, IExporterDependencies From 9da44973da1443353815fd97ab4a84346a518c2a Mon Sep 17 00:00:00 2001 From: nietras Date: Thu, 17 Aug 2023 14:39:31 +0200 Subject: [PATCH 05/15] Make MarkdownExporter ctor and Dialect protected (#2407) Make MarkdownExporter ctor and Dialect protected --- src/BenchmarkDotNet/Exporters/MarkdownExporter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs b/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs index 1d3bc5171b..096ae84b52 100644 --- a/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs +++ b/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs @@ -22,7 +22,7 @@ public enum MarkdownHighlightStrategy protected override string FileExtension => "md"; protected override string FileNameSuffix => $"-{Dialect.ToLower()}"; - private string Dialect { get; set; } + protected string Dialect { get; set; } public static readonly IExporter Default = new MarkdownExporter { @@ -86,7 +86,7 @@ public enum MarkdownHighlightStrategy [PublicAPI] protected string BoldMarkupFormat = "**{0}**"; [PublicAPI] protected bool EscapeHtml; - private MarkdownExporter() { } + protected MarkdownExporter() { } public override void ExportToLog(Summary summary, ILogger logger) { From c35dcb2b949096d257c743672ce7f2fd276828ad Mon Sep 17 00:00:00 2001 From: nietras Date: Sat, 19 Aug 2023 01:24:57 +0200 Subject: [PATCH 06/15] Refactor out base TextLogger from StreamLogger (#2406) * Refactor out base TextWriterLogger from StreamLogger * rename to TextLogger * fix Id * rearrange --- src/BenchmarkDotNet/Loggers/StreamLogger.cs | 25 ++++++--------------- src/BenchmarkDotNet/Loggers/TextLogger.cs | 25 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 18 deletions(-) create mode 100644 src/BenchmarkDotNet/Loggers/TextLogger.cs diff --git a/src/BenchmarkDotNet/Loggers/StreamLogger.cs b/src/BenchmarkDotNet/Loggers/StreamLogger.cs index d9c9bc2b10..7e7730de5f 100644 --- a/src/BenchmarkDotNet/Loggers/StreamLogger.cs +++ b/src/BenchmarkDotNet/Loggers/StreamLogger.cs @@ -1,28 +1,17 @@ -using System; -using System.IO; +using System.IO; using JetBrains.Annotations; namespace BenchmarkDotNet.Loggers { - public class StreamLogger : ILogger, IDisposable + public class StreamLogger : TextLogger { - private readonly StreamWriter writer; - - public StreamLogger(StreamWriter writer) => this.writer = writer; - - public void Dispose() => writer.Dispose(); + public StreamLogger(StreamWriter writer) : base(writer) { } [PublicAPI] - public StreamLogger(string filePath, bool append = false) => writer = new StreamWriter(filePath, append); - - public string Id => nameof(StreamLogger); - public int Priority => 0; - public void Write(LogKind logKind, string text) => writer.Write(text); - - public void WriteLine() => writer.WriteLine(); - - public void WriteLine(LogKind logKind, string text) => writer.WriteLine(text); + public StreamLogger(string filePath, bool append = false) + : this(new StreamWriter(filePath, append)) + { } - public void Flush() => writer.Flush(); + public override string Id => nameof(StreamLogger); } } diff --git a/src/BenchmarkDotNet/Loggers/TextLogger.cs b/src/BenchmarkDotNet/Loggers/TextLogger.cs new file mode 100644 index 0000000000..e261f07f26 --- /dev/null +++ b/src/BenchmarkDotNet/Loggers/TextLogger.cs @@ -0,0 +1,25 @@ +using System; +using System.IO; + +namespace BenchmarkDotNet.Loggers +{ + public class TextLogger : ILogger, IDisposable + { + private readonly TextWriter writer; + + public TextLogger(TextWriter writer) => this.writer = writer; + + public virtual string Id => nameof(TextLogger); + public int Priority => 0; + + public void Write(LogKind logKind, string text) => writer.Write(text); + + public void WriteLine() => writer.WriteLine(); + + public void WriteLine(LogKind logKind, string text) => writer.WriteLine(text); + + public void Flush() => writer.Flush(); + + public void Dispose() => writer.Dispose(); + } +} From e0c667f6363e75f4e18e34767b6211d360962873 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 22 Aug 2023 15:24:51 -0400 Subject: [PATCH 07/15] - update the templates install command to reflect dotnet cli updates (#2415) --- docs/articles/guides/dotnet-new-templates.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/articles/guides/dotnet-new-templates.md b/docs/articles/guides/dotnet-new-templates.md index e76ae70c4e..dace6641ee 100644 --- a/docs/articles/guides/dotnet-new-templates.md +++ b/docs/articles/guides/dotnet-new-templates.md @@ -13,13 +13,13 @@ The template exists for each major .NET language ([C#](https://learn.microsoft.c The templates requires the [.NET Core SDK](https://www.microsoft.com/net/download). Once installed, run the following command to install the templates: ```log -dotnet new -i BenchmarkDotNet.Templates +dotnet new install BenchmarkDotNet.Templates ``` If you want to uninstall all BenchmarkDotNet templates: ```log -dotnet new -u BenchmarkDotNet.Templates +dotnet new uninstall BenchmarkDotNet.Templates ``` The template is a nuget package distributed over nuget: [BenchmarkDotNet.Templates](https://www.nuget.org/packages/BenchmarkDotNet.Templates/). From d391085c4a3298d3f9223197f986823f01c34e37 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 25 Aug 2023 11:15:04 +0200 Subject: [PATCH 08/15] Update stub decoding for .NET 8 for disassemblers (#2416) * Update stub decoding for .NET 8 for disassemblers The call counting stub, stub precode and fixup precode were modified in .NET 8 to have larger size of the interleaved code / data blocks. The stub decoder in the intel and arm64 disassemblers need to be updated to take that into account. --- .../Disassemblers/Arm64Disassembler.cs | 94 +++++++++++-------- .../Disassemblers/IntelDisassembler.cs | 70 +++++++++----- 2 files changed, 105 insertions(+), 59 deletions(-) diff --git a/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs b/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs index bd4bc112f9..5b15a1e6ef 100644 --- a/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs +++ b/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs @@ -141,45 +141,63 @@ public void Feed(Arm64Instruction instruction) internal class Arm64Disassembler : ClrMdV2Disassembler { - // See dotnet/runtime src/coreclr/vm/arm64/thunktemplates.asm/.S for the stub code - // ldr x9, DATA_SLOT(CallCountingStub, RemainingCallCountCell) - // ldrh w10, [x9] - // subs w10, w10, #0x1 - private static byte[] callCountingStubTemplate = new byte[12] { 0x09, 0x00, 0x00, 0x58, 0x2a, 0x01, 0x40, 0x79, 0x4a, 0x05, 0x00, 0x71 }; - // ldr x10, DATA_SLOT(StubPrecode, Target) - // ldr x12, DATA_SLOT(StubPrecode, MethodDesc) - // br x10 - private static byte[] stubPrecodeTemplate = new byte[12] { 0x4a, 0x00, 0x00, 0x58, 0xec, 0x00, 0x00, 0x58, 0x40, 0x01, 0x1f, 0xd6 }; - // ldr x11, DATA_SLOT(FixupPrecode, Target) - // br x11 - // ldr x12, DATA_SLOT(FixupPrecode, MethodDesc) - private static byte[] fixupPrecodeTemplate = new byte[12] { 0x0b, 0x00, 0x00, 0x58, 0x60, 0x01, 0x1f, 0xd6, 0x0c, 0x00, 0x00, 0x58 }; - - static Arm64Disassembler() + internal sealed class RuntimeSpecificData { - // The stubs code depends on the current OS memory page size, so we need to update the templates to reflect that - int pageSizeShifted = Environment.SystemPageSize / 32; - // Calculate the ldr x9, #offset instruction with offset based on the page size - callCountingStubTemplate[1] = (byte)(pageSizeShifted & 0xff); - callCountingStubTemplate[2] = (byte)(pageSizeShifted >> 8); + // See dotnet/runtime src/coreclr/vm/arm64/thunktemplates.asm/.S for the stub code + // ldr x9, DATA_SLOT(CallCountingStub, RemainingCallCountCell) + // ldrh w10, [x9] + // subs w10, w10, #0x1 + internal readonly byte[] callCountingStubTemplate = new byte[12] { 0x09, 0x00, 0x00, 0x58, 0x2a, 0x01, 0x40, 0x79, 0x4a, 0x05, 0x00, 0x71 }; + // ldr x10, DATA_SLOT(StubPrecode, Target) + // ldr x12, DATA_SLOT(StubPrecode, MethodDesc) + // br x10 + internal readonly byte[] stubPrecodeTemplate = new byte[12] { 0x4a, 0x00, 0x00, 0x58, 0xec, 0x00, 0x00, 0x58, 0x40, 0x01, 0x1f, 0xd6 }; + // ldr x11, DATA_SLOT(FixupPrecode, Target) + // br x11 + // ldr x12, DATA_SLOT(FixupPrecode, MethodDesc) + internal readonly byte[] fixupPrecodeTemplate = new byte[12] { 0x0b, 0x00, 0x00, 0x58, 0x60, 0x01, 0x1f, 0xd6, 0x0c, 0x00, 0x00, 0x58 }; + internal readonly ulong stubPageSize; + + internal RuntimeSpecificData(State state) + { + stubPageSize = (ulong)Environment.SystemPageSize; + if (state.RuntimeVersion.Major >= 8) + { + // In .NET 8, the stub page size was changed to min 16kB + stubPageSize = Math.Max(stubPageSize, 16384); + } + + // The stubs code depends on the current OS memory page size, so we need to update the templates to reflect that + ulong pageSizeShifted = stubPageSize / 32; + // Calculate the ldr x9, #offset instruction with offset based on the page size + callCountingStubTemplate[1] = (byte)(pageSizeShifted & 0xff); + callCountingStubTemplate[2] = (byte)(pageSizeShifted >> 8); - // Calculate the ldr x10, #offset instruction with offset based on the page size - stubPrecodeTemplate[1] = (byte)(pageSizeShifted & 0xff); - stubPrecodeTemplate[2] = (byte)(pageSizeShifted >> 8); - // Calculate the ldr x12, #offset instruction with offset based on the page size - stubPrecodeTemplate[5] = (byte)((pageSizeShifted - 1) & 0xff); - stubPrecodeTemplate[6] = (byte)((pageSizeShifted - 1) >> 8); + // Calculate the ldr x10, #offset instruction with offset based on the page size + stubPrecodeTemplate[1] = (byte)(pageSizeShifted & 0xff); + stubPrecodeTemplate[2] = (byte)(pageSizeShifted >> 8); + // Calculate the ldr x12, #offset instruction with offset based on the page size + stubPrecodeTemplate[5] = (byte)((pageSizeShifted - 1) & 0xff); + stubPrecodeTemplate[6] = (byte)((pageSizeShifted - 1) >> 8); - // Calculate the ldr x11, #offset instruction with offset based on the page size - fixupPrecodeTemplate[1] = (byte)(pageSizeShifted & 0xff); - fixupPrecodeTemplate[2] = (byte)(pageSizeShifted >> 8); - // Calculate the ldr x12, #offset instruction with offset based on the page size - fixupPrecodeTemplate[9] = (byte)(pageSizeShifted & 0xff); - fixupPrecodeTemplate[10] = (byte)(pageSizeShifted >> 8); + // Calculate the ldr x11, #offset instruction with offset based on the page size + fixupPrecodeTemplate[1] = (byte)(pageSizeShifted & 0xff); + fixupPrecodeTemplate[2] = (byte)(pageSizeShifted >> 8); + // Calculate the ldr x12, #offset instruction with offset based on the page size + fixupPrecodeTemplate[9] = (byte)(pageSizeShifted & 0xff); + fixupPrecodeTemplate[10] = (byte)(pageSizeShifted >> 8); + } } + private static readonly Dictionary runtimeSpecificData = new (); + protected override IEnumerable Decode(byte[] code, ulong startAddress, State state, int depth, ClrMethod currentMethod, DisassemblySyntax syntax) { + if (!runtimeSpecificData.TryGetValue(state.RuntimeVersion, out RuntimeSpecificData data)) + { + runtimeSpecificData.Add(state.RuntimeVersion, data = new RuntimeSpecificData(state)); + } + const Arm64DisassembleMode disassembleMode = Arm64DisassembleMode.Arm; using (CapstoneArm64Disassembler disassembler = CapstoneDisassembler.CreateArm64Disassembler(disassembleMode)) { @@ -210,21 +228,21 @@ protected override IEnumerable Decode(byte[] code, ulong startAddress, Stat if (state.Runtime.DataTarget.DataReader.Read(address, buffer) == buffer.Length) { - if (buffer.SequenceEqual(callCountingStubTemplate)) + if (buffer.SequenceEqual(data.callCountingStubTemplate)) { const ulong TargetMethodAddressSlotOffset = 8; - address = state.Runtime.DataTarget.DataReader.ReadPointer(address + (ulong)Environment.SystemPageSize + TargetMethodAddressSlotOffset); + address = state.Runtime.DataTarget.DataReader.ReadPointer(address + data.stubPageSize + TargetMethodAddressSlotOffset); } - else if (buffer.SequenceEqual(stubPrecodeTemplate)) + else if (buffer.SequenceEqual(data.stubPrecodeTemplate)) { const ulong MethodDescSlotOffset = 0; - address = state.Runtime.DataTarget.DataReader.ReadPointer(address + (ulong)Environment.SystemPageSize + MethodDescSlotOffset); + address = state.Runtime.DataTarget.DataReader.ReadPointer(address + data.stubPageSize + MethodDescSlotOffset); isPrestubMD = true; } - else if (buffer.SequenceEqual(fixupPrecodeTemplate)) + else if (buffer.SequenceEqual(data.fixupPrecodeTemplate)) { const ulong MethodDescSlotOffset = 8; - address = state.Runtime.DataTarget.DataReader.ReadPointer(address + (ulong)Environment.SystemPageSize + MethodDescSlotOffset); + address = state.Runtime.DataTarget.DataReader.ReadPointer(address + data.stubPageSize + MethodDescSlotOffset); isPrestubMD = true; } } diff --git a/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs b/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs index bd3227dbac..2c024f587d 100644 --- a/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs +++ b/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs @@ -9,24 +9,52 @@ namespace BenchmarkDotNet.Disassemblers { internal class IntelDisassembler : ClrMdV2Disassembler { - // See dotnet/runtime src/coreclr/vm/amd64/thunktemplates.asm/.S for the stub code - // mov rax,QWORD PTR [rip + DATA_SLOT(CallCountingStub, RemainingCallCountCell)] - // dec WORD PTR [rax] - // je LOCAL_LABEL(CountReachedZero) - // jmp QWORD PTR [rip + DATA_SLOT(CallCountingStub, TargetForMethod)] - // LOCAL_LABEL(CountReachedZero): - // jmp QWORD PTR [rip + DATA_SLOT(CallCountingStub, TargetForThresholdReached)] - private static byte[] callCountingStubTemplate = new byte[10] { 0x48, 0x8b, 0x05, 0xf9, 0x0f, 0x00, 0x00, 0x66, 0xff, 0x08 }; - // mov r10, [rip + DATA_SLOT(StubPrecode, MethodDesc)] - // jmp [rip + DATA_SLOT(StubPrecode, Target)] - private static byte[] stubPrecodeTemplate = new byte[13] { 0x4c, 0x8b, 0x15, 0xf9, 0x0f, 0x00, 0x00, 0xff, 0x25, 0xfb, 0x0f, 0x00, 0x00 }; - // jmp [rip + DATA_SLOT(FixupPrecode, Target)] - // mov r10, [rip + DATA_SLOT(FixupPrecode, MethodDesc)] - // jmp [rip + DATA_SLOT(FixupPrecode, PrecodeFixupThunk)] - private static byte[] fixupPrecodeTemplate = new byte[19] { 0xff, 0x25, 0xfa, 0x0f, 0x00, 0x00, 0x4c, 0x8b, 0x15, 0xfb, 0x0f, 0x00, 0x00, 0xff, 0x25, 0xfd, 0x0f, 0x00, 0x00 }; + internal sealed class RuntimeSpecificData + { + // See dotnet/runtime src/coreclr/vm/amd64/thunktemplates.asm/.S for the stub code + // mov rax,QWORD PTR [rip + DATA_SLOT(CallCountingStub, RemainingCallCountCell)] + // dec WORD PTR [rax] + // je LOCAL_LABEL(CountReachedZero) + // jmp QWORD PTR [rip + DATA_SLOT(CallCountingStub, TargetForMethod)] + // LOCAL_LABEL(CountReachedZero): + // jmp QWORD PTR [rip + DATA_SLOT(CallCountingStub, TargetForThresholdReached)] + internal readonly byte[] callCountingStubTemplate = new byte[10] { 0x48, 0x8b, 0x05, 0xf9, 0x0f, 0x00, 0x00, 0x66, 0xff, 0x08 }; + // mov r10, [rip + DATA_SLOT(StubPrecode, MethodDesc)] + // jmp [rip + DATA_SLOT(StubPrecode, Target)] + internal readonly byte[] stubPrecodeTemplate = new byte[13] { 0x4c, 0x8b, 0x15, 0xf9, 0x0f, 0x00, 0x00, 0xff, 0x25, 0xfb, 0x0f, 0x00, 0x00 }; + // jmp [rip + DATA_SLOT(FixupPrecode, Target)] + // mov r10, [rip + DATA_SLOT(FixupPrecode, MethodDesc)] + // jmp [rip + DATA_SLOT(FixupPrecode, PrecodeFixupThunk)] + internal readonly byte[] fixupPrecodeTemplate = new byte[19] { 0xff, 0x25, 0xfa, 0x0f, 0x00, 0x00, 0x4c, 0x8b, 0x15, 0xfb, 0x0f, 0x00, 0x00, 0xff, 0x25, 0xfd, 0x0f, 0x00, 0x00 }; + internal readonly ulong stubPageSize; + + internal RuntimeSpecificData(State state) + { + stubPageSize = (ulong)Environment.SystemPageSize; + if (state.RuntimeVersion.Major >= 8) + { + // In .NET 8, the stub page size was changed to 16kB + stubPageSize = 16384; + // Update the templates so that the offsets are correct + callCountingStubTemplate[4] = 0x3f; + stubPrecodeTemplate[4] = 0x3f; + stubPrecodeTemplate[10] = 0x3f; + fixupPrecodeTemplate[3] = 0x3f; + fixupPrecodeTemplate[10] = 0x3f; + fixupPrecodeTemplate[16] = 0x3f; + } + } + } + + private static readonly Dictionary runtimeSpecificData = new (); protected override IEnumerable Decode(byte[] code, ulong startAddress, State state, int depth, ClrMethod currentMethod, DisassemblySyntax syntax) { + if (!runtimeSpecificData.TryGetValue(state.RuntimeVersion, out RuntimeSpecificData data)) + { + runtimeSpecificData.Add(state.RuntimeVersion, data = new RuntimeSpecificData(state)); + } + var reader = new ByteArrayCodeReader(code); var decoder = Decoder.Create(state.Runtime.DataTarget.DataReader.PointerSize * 8, reader); decoder.IP = startAddress; @@ -53,27 +81,27 @@ protected override IEnumerable Decode(byte[] code, ulong startAddress, Stat FlushCachedDataIfNeeded(state.Runtime.DataTarget.DataReader, address, buffer); - if (state.Runtime.DataTarget.DataReader.Read(address, buffer) == buffer.Length && buffer.SequenceEqual(callCountingStubTemplate)) + if (state.Runtime.DataTarget.DataReader.Read(address, buffer) == buffer.Length && buffer.SequenceEqual(data.callCountingStubTemplate)) { const ulong TargetMethodAddressSlotOffset = 8; - address = state.Runtime.DataTarget.DataReader.ReadPointer(address + (ulong)Environment.SystemPageSize + TargetMethodAddressSlotOffset); + address = state.Runtime.DataTarget.DataReader.ReadPointer(address + data.stubPageSize + TargetMethodAddressSlotOffset); } else { buffer = new byte[13]; - if (state.Runtime.DataTarget.DataReader.Read(address, buffer) == buffer.Length && buffer.SequenceEqual(stubPrecodeTemplate)) + if (state.Runtime.DataTarget.DataReader.Read(address, buffer) == buffer.Length && buffer.SequenceEqual(data.stubPrecodeTemplate)) { const ulong MethodDescSlotOffset = 0; - address = state.Runtime.DataTarget.DataReader.ReadPointer(address + (ulong)Environment.SystemPageSize + MethodDescSlotOffset); + address = state.Runtime.DataTarget.DataReader.ReadPointer(address + data.stubPageSize + MethodDescSlotOffset); isPrestubMD = true; } else { buffer = new byte[19]; - if (state.Runtime.DataTarget.DataReader.Read(address, buffer) == buffer.Length && buffer.SequenceEqual(fixupPrecodeTemplate)) + if (state.Runtime.DataTarget.DataReader.Read(address, buffer) == buffer.Length && buffer.SequenceEqual(data.fixupPrecodeTemplate)) { const ulong MethodDescSlotOffset = 8; - address = state.Runtime.DataTarget.DataReader.ReadPointer(address + (ulong)Environment.SystemPageSize + MethodDescSlotOffset); + address = state.Runtime.DataTarget.DataReader.ReadPointer(address + data.stubPageSize + MethodDescSlotOffset); isPrestubMD = true; } From 2d763cf1dfabd90bb47d169981aca2f0269f6a02 Mon Sep 17 00:00:00 2001 From: Alina Smirnova Date: Sun, 27 Aug 2023 14:26:56 +0200 Subject: [PATCH 09/15] Enable nullability for BenchmarkDotNet.Annotations --- .../Attributes/ArgumentsAttribute.cs | 6 +- .../Attributes/BenchmarkAttribute.cs | 2 +- .../Attributes/BenchmarkCategoryAttribute.cs | 2 +- .../Attributes/ParamsAttribute.cs | 6 +- .../BenchmarkDotNet.Annotations.csproj | 1 + .../Helpers/CodeAnnotations.cs | 115 +++++++++--------- 6 files changed, 67 insertions(+), 65 deletions(-) diff --git a/src/BenchmarkDotNet.Annotations/Attributes/ArgumentsAttribute.cs b/src/BenchmarkDotNet.Annotations/Attributes/ArgumentsAttribute.cs index 6caa31a91a..c9b8e7961c 100644 --- a/src/BenchmarkDotNet.Annotations/Attributes/ArgumentsAttribute.cs +++ b/src/BenchmarkDotNet.Annotations/Attributes/ArgumentsAttribute.cs @@ -6,13 +6,13 @@ namespace BenchmarkDotNet.Attributes [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class ArgumentsAttribute : PriorityAttribute { - public object[] Values { get; } + public object?[] Values { get; } // CLS-Compliant Code requires a constructor without an array in the argument list [PublicAPI] public ArgumentsAttribute() => Values = new object[0]; - public ArgumentsAttribute(params object[] values) - => Values = values ?? new object[] { null }; // when users do Arguments(null) they mean one, null argument + public ArgumentsAttribute(params object?[]? values) + => Values = values ?? new object?[] { null }; // when users do Arguments(null) they mean one, null argument } } \ No newline at end of file diff --git a/src/BenchmarkDotNet.Annotations/Attributes/BenchmarkAttribute.cs b/src/BenchmarkDotNet.Annotations/Attributes/BenchmarkAttribute.cs index 377e737b96..55354dd52b 100644 --- a/src/BenchmarkDotNet.Annotations/Attributes/BenchmarkAttribute.cs +++ b/src/BenchmarkDotNet.Annotations/Attributes/BenchmarkAttribute.cs @@ -14,7 +14,7 @@ public BenchmarkAttribute([CallerLineNumber] int sourceCodeLineNumber = 0, [Call SourceCodeFile = sourceCodeFile; } - public string Description { get; set; } + public string? Description { get; set; } public bool Baseline { get; set; } diff --git a/src/BenchmarkDotNet.Annotations/Attributes/BenchmarkCategoryAttribute.cs b/src/BenchmarkDotNet.Annotations/Attributes/BenchmarkCategoryAttribute.cs index f386d6cae4..332fe7d176 100644 --- a/src/BenchmarkDotNet.Annotations/Attributes/BenchmarkCategoryAttribute.cs +++ b/src/BenchmarkDotNet.Annotations/Attributes/BenchmarkCategoryAttribute.cs @@ -9,7 +9,7 @@ public class BenchmarkCategoryAttribute : Attribute public string[] Categories { get; } // CLS-Compliant Code requires a constructor without an array in the argument list - [PublicAPI] protected BenchmarkCategoryAttribute() { } + [PublicAPI] protected BenchmarkCategoryAttribute() => Categories = new string[0]; public BenchmarkCategoryAttribute(params string[] categories) => Categories = categories; } diff --git a/src/BenchmarkDotNet.Annotations/Attributes/ParamsAttribute.cs b/src/BenchmarkDotNet.Annotations/Attributes/ParamsAttribute.cs index 6911657d5e..22148ffa64 100644 --- a/src/BenchmarkDotNet.Annotations/Attributes/ParamsAttribute.cs +++ b/src/BenchmarkDotNet.Annotations/Attributes/ParamsAttribute.cs @@ -5,12 +5,12 @@ namespace BenchmarkDotNet.Attributes [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class ParamsAttribute : PriorityAttribute { - public object[] Values { get; } + public object?[] Values { get; } // CLS-Compliant Code requires a constructor without an array in the argument list public ParamsAttribute() => Values = new object[0]; - public ParamsAttribute(params object[] values) - => Values = values ?? new object[] { null }; // when users do Params(null) they mean one, null argument + public ParamsAttribute(params object?[]? values) + => Values = values ?? new object?[] { null }; // when users do Params(null) they mean one, null argument } } diff --git a/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj b/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj index 4848f1009b..2e7361e163 100644 --- a/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj +++ b/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj @@ -9,6 +9,7 @@ BenchmarkDotNet True + enable diff --git a/src/BenchmarkDotNet/Helpers/CodeAnnotations.cs b/src/BenchmarkDotNet/Helpers/CodeAnnotations.cs index 898e44cb32..b8ddc0fabb 100644 --- a/src/BenchmarkDotNet/Helpers/CodeAnnotations.cs +++ b/src/BenchmarkDotNet/Helpers/CodeAnnotations.cs @@ -20,6 +20,7 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#nullable enable using System; // ReSharper disable All @@ -126,12 +127,12 @@ internal sealed class StringFormatMethodAttribute : Attribute /// /// Specifies which parameter of an annotated method should be treated as the format string /// - public StringFormatMethodAttribute([NotNull] string formatParameterName) + public StringFormatMethodAttribute(string formatParameterName) { FormatParameterName = formatParameterName; } - [NotNull] public string FormatParameterName { get; } + public string FormatParameterName { get; } } /// @@ -166,12 +167,12 @@ public StringFormatMethodAttribute([NotNull] string formatParameterName) AllowMultiple = true)] internal sealed class ValueProviderAttribute : Attribute { - public ValueProviderAttribute([NotNull] string name) + public ValueProviderAttribute(string name) { Name = name; } - [NotNull] public string Name { get; } + public string Name { get; } } /// @@ -231,12 +232,12 @@ internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute { public NotifyPropertyChangedInvocatorAttribute() { } - public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) + public NotifyPropertyChangedInvocatorAttribute(string parameterName) { ParameterName = parameterName; } - [CanBeNull] public string ParameterName { get; } + public string? ParameterName { get; } } /// @@ -286,16 +287,16 @@ public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] internal sealed class ContractAnnotationAttribute : Attribute { - public ContractAnnotationAttribute([NotNull] string contract) + public ContractAnnotationAttribute(string contract) : this(contract, false) { } - public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + public ContractAnnotationAttribute(string contract, bool forceFullStates) { Contract = contract; ForceFullStates = forceFullStates; } - [NotNull] public string Contract { get; } + public string Contract { get; } public bool ForceFullStates { get; } } @@ -360,12 +361,12 @@ internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { } [BaseTypeRequired(typeof(Attribute))] internal sealed class BaseTypeRequiredAttribute : Attribute { - public BaseTypeRequiredAttribute([NotNull] Type baseType) + public BaseTypeRequiredAttribute(Type baseType) { BaseType = baseType; } - [NotNull] public Type BaseType { get; } + public Type BaseType { get; } } /// @@ -476,12 +477,12 @@ internal sealed class PublicAPIAttribute : Attribute { public PublicAPIAttribute() { } - public PublicAPIAttribute([NotNull] string comment) + public PublicAPIAttribute(string comment) { Comment = comment; } - [CanBeNull] public string Comment { get; } + public string? Comment { get; } } /// @@ -522,12 +523,12 @@ internal sealed class MustUseReturnValueAttribute : Attribute { public MustUseReturnValueAttribute() { } - public MustUseReturnValueAttribute([NotNull] string justification) + public MustUseReturnValueAttribute(string justification) { Justification = justification; } - [CanBeNull] public string Justification { get; } + public string? Justification { get; } } /// @@ -559,12 +560,12 @@ internal sealed class PathReferenceAttribute : Attribute { public PathReferenceAttribute() { } - public PathReferenceAttribute([NotNull, PathReference] string basePath) + public PathReferenceAttribute([PathReference] string basePath) { BasePath = basePath; } - [CanBeNull] public string BasePath { get; } + public string? BasePath { get; } } /// @@ -628,7 +629,7 @@ internal sealed class MacroAttribute : Attribute /// Allows specifying a macro that will be executed for a source template /// parameter when the template is expanded. /// - [CanBeNull] public string Expression { get; set; } + public string? Expression { get; set; } /// /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. @@ -644,73 +645,73 @@ internal sealed class MacroAttribute : Attribute /// Identifies the target parameter of a source template if the /// is applied on a template method. /// - [CanBeNull] public string Target { get; set; } + public string? Target { get; set; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute { - public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) + public AspMvcAreaMasterLocationFormatAttribute(string format) { Format = format; } - [NotNull] public string Format { get; } + public string Format { get; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute { - public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) + public AspMvcAreaPartialViewLocationFormatAttribute(string format) { Format = format; } - [NotNull] public string Format { get; } + public string Format { get; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute { - public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) + public AspMvcAreaViewLocationFormatAttribute(string format) { Format = format; } - [NotNull] public string Format { get; } + public string Format { get; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcMasterLocationFormatAttribute : Attribute { - public AspMvcMasterLocationFormatAttribute([NotNull] string format) + public AspMvcMasterLocationFormatAttribute(string format) { Format = format; } - [NotNull] public string Format { get; } + public string Format { get; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute { - public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) + public AspMvcPartialViewLocationFormatAttribute(string format) { Format = format; } - [NotNull] public string Format { get; } + public string Format { get; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcViewLocationFormatAttribute : Attribute { - public AspMvcViewLocationFormatAttribute([NotNull] string format) + public AspMvcViewLocationFormatAttribute(string format) { Format = format; } - [NotNull] public string Format { get; } + public string Format { get; } } /// @@ -724,12 +725,12 @@ internal sealed class AspMvcActionAttribute : Attribute { public AspMvcActionAttribute() { } - public AspMvcActionAttribute([NotNull] string anonymousProperty) + public AspMvcActionAttribute(string anonymousProperty) { AnonymousProperty = anonymousProperty; } - [CanBeNull] public string AnonymousProperty { get; } + public string? AnonymousProperty { get; } } /// @@ -742,12 +743,12 @@ internal sealed class AspMvcAreaAttribute : Attribute { public AspMvcAreaAttribute() { } - public AspMvcAreaAttribute([NotNull] string anonymousProperty) + public AspMvcAreaAttribute(string anonymousProperty) { AnonymousProperty = anonymousProperty; } - [CanBeNull] public string AnonymousProperty { get; } + public string? AnonymousProperty { get; } } /// @@ -761,12 +762,12 @@ internal sealed class AspMvcControllerAttribute : Attribute { public AspMvcControllerAttribute() { } - public AspMvcControllerAttribute([NotNull] string anonymousProperty) + public AspMvcControllerAttribute(string anonymousProperty) { AnonymousProperty = anonymousProperty; } - [CanBeNull] public string AnonymousProperty { get; } + public string? AnonymousProperty { get; } } /// @@ -864,23 +865,23 @@ internal sealed class HtmlElementAttributesAttribute : Attribute { public HtmlElementAttributesAttribute() { } - public HtmlElementAttributesAttribute([NotNull] string name) + public HtmlElementAttributesAttribute(string name) { Name = name; } - [CanBeNull] public string Name { get; } + public string? Name { get; } } [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class HtmlAttributeValueAttribute : Attribute { - public HtmlAttributeValueAttribute([NotNull] string name) + public HtmlAttributeValueAttribute(string name) { Name = name; } - [NotNull] public string Name { get; } + public string Name { get; } } /// @@ -1065,15 +1066,15 @@ internal sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] internal sealed class AspChildControlTypeAttribute : Attribute { - public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) + public AspChildControlTypeAttribute(string tagName, Type controlType) { TagName = tagName; ControlType = controlType; } - [NotNull] public string TagName { get; } + public string TagName { get; } - [NotNull] public Type ControlType { get; } + public Type ControlType { get; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] @@ -1088,12 +1089,12 @@ internal sealed class AspMethodPropertyAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] internal sealed class AspRequiredAttributeAttribute : Attribute { - public AspRequiredAttributeAttribute([NotNull] string attribute) + public AspRequiredAttributeAttribute(string attribute) { Attribute = attribute; } - [NotNull] public string Attribute { get; } + public string Attribute { get; } } [AttributeUsage(AttributeTargets.Property)] @@ -1110,55 +1111,55 @@ public AspTypePropertyAttribute(bool createConstructorReferences) [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorImportNamespaceAttribute : Attribute { - public RazorImportNamespaceAttribute([NotNull] string name) + public RazorImportNamespaceAttribute(string name) { Name = name; } - [NotNull] public string Name { get; } + public string Name { get; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorInjectionAttribute : Attribute { - public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) + public RazorInjectionAttribute(string type, string fieldName) { Type = type; FieldName = fieldName; } - [NotNull] public string Type { get; } + public string Type { get; } - [NotNull] public string FieldName { get; } + public string FieldName { get; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorDirectiveAttribute : Attribute { - public RazorDirectiveAttribute([NotNull] string directive) + public RazorDirectiveAttribute(string directive) { Directive = directive; } - [NotNull] public string Directive { get; } + public string Directive { get; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorPageBaseTypeAttribute : Attribute { - public RazorPageBaseTypeAttribute([NotNull] string baseType) + public RazorPageBaseTypeAttribute(string baseType) { BaseType = baseType; } - public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) + public RazorPageBaseTypeAttribute(string baseType, string pageName) { BaseType = baseType; PageName = pageName; } - [NotNull] public string BaseType { get; } - [CanBeNull] public string PageName { get; } + public string BaseType { get; } + public string? PageName { get; } } [AttributeUsage(AttributeTargets.Method)] From 83fc5ed5433109a7129f696cbe6ab35fd6e0810f Mon Sep 17 00:00:00 2001 From: Alina Smirnova Date: Sun, 27 Aug 2023 16:00:20 +0200 Subject: [PATCH 10/15] Updated CodeAnnotations to the actual version --- .../Helpers/CodeAnnotations.cs | 2591 ++++++++++------- 1 file changed, 1606 insertions(+), 985 deletions(-) diff --git a/src/BenchmarkDotNet/Helpers/CodeAnnotations.cs b/src/BenchmarkDotNet/Helpers/CodeAnnotations.cs index b8ddc0fabb..cc5c24ed71 100644 --- a/src/BenchmarkDotNet/Helpers/CodeAnnotations.cs +++ b/src/BenchmarkDotNet/Helpers/CodeAnnotations.cs @@ -20,10 +20,10 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#nullable enable -using System; +#nullable disable -// ReSharper disable All +using System; +// ReSharper disable UnusedType.Global #pragma warning disable 1591 // ReSharper disable UnusedMember.Global @@ -35,1145 +35,1766 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace JetBrains.Annotations { - /// - /// Indicates that the value of the marked element could be null sometimes, - /// so checking for null is required before its usage. - /// - /// - /// [CanBeNull] object Test() => null; - /// - /// void UseTest() { - /// var p = Test(); - /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] - internal sealed class CanBeNullAttribute : Attribute { } - - /// - /// Indicates that the value of the marked element can never be null. - /// - /// - /// [NotNull] object Foo() { - /// return null; // Warning: Possible 'null' assignment - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] - internal sealed class NotNullAttribute : Attribute { } - - /// - /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task - /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property - /// or of the Lazy.Value property can never be null. - /// - /// - /// public void Foo([ItemNotNull]List<string> books) - /// { - /// foreach (var book in books) { - /// if (book != null) // Warning: Expression is always true - /// Console.WriteLine(book.ToUpper()); - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field)] - internal sealed class ItemNotNullAttribute : Attribute { } - - /// - /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task - /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property - /// or of the Lazy.Value property can be null. - /// - /// - /// public void Foo([ItemCanBeNull]List<string> books) - /// { - /// foreach (var book in books) - /// { - /// // Warning: Possible 'System.NullReferenceException' - /// Console.WriteLine(book.ToUpper()); - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field)] - internal sealed class ItemCanBeNullAttribute : Attribute { } - - /// - /// Indicates that the marked method builds string by the format pattern and (optional) arguments. - /// The parameter, which contains the format string, should be given in constructor. The format string - /// should be in -like form. - /// - /// - /// [StringFormatMethod("message")] - /// void ShowError(string message, params object[] args) { /* do something */ } - /// - /// void Foo() { - /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string - /// } - /// - [AttributeUsage( - AttributeTargets.Constructor | AttributeTargets.Method | - AttributeTargets.Property | AttributeTargets.Delegate)] - internal sealed class StringFormatMethodAttribute : Attribute + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so checking for null is required before its usage. + /// + /// + /// [CanBeNull] object Test() => null; + /// + /// void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] +internal sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element can never be null. + /// + /// + /// [NotNull] object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] +internal sealed class NotNullAttribute : Attribute { } + + /// + /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can never be null. + /// + /// + /// public void Foo([ItemNotNull]List<string> books) + /// { + /// foreach (var book in books) { + /// if (book != null) // Warning: Expression is always true + /// Console.WriteLine(book.ToUpper()); + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] +internal sealed class ItemNotNullAttribute : Attribute { } + + /// + /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can be null. + /// + /// + /// public void Foo([ItemCanBeNull]List<string> books) + /// { + /// foreach (var book in books) + /// { + /// // Warning: Possible 'System.NullReferenceException' + /// Console.WriteLine(book.ToUpper()); + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] +internal sealed class ItemCanBeNullAttribute : Attribute { } + + /// + /// Indicates that the marked method builds a string by the format pattern and (optional) arguments. + /// The parameter, which contains the format string, should be given in the constructor. The format string + /// should be in -like form. + /// + /// + /// [StringFormatMethod("message")] + /// void ShowError(string message, params object[] args) { /* do something */ } + /// + /// void Foo() { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | + AttributeTargets.Property | AttributeTargets.Delegate)] +internal sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Specifies which parameter of an annotated method should be treated as the format string. + /// + public StringFormatMethodAttribute([NotNull] string formatParameterName) { - /// - /// Specifies which parameter of an annotated method should be treated as the format string - /// - public StringFormatMethodAttribute(string formatParameterName) - { - FormatParameterName = formatParameterName; - } - - public string FormatParameterName { get; } + FormatParameterName = formatParameterName; } - /// - /// Use this annotation to specify a type that contains static or const fields - /// with values for the annotated property/field/parameter. - /// The specified type will be used to improve completion suggestions. - /// - /// - /// namespace TestNamespace - /// { - /// public class Constants - /// { - /// public static int INT_CONST = 1; - /// public const string STRING_CONST = "1"; - /// } - /// - /// public class Class1 - /// { - /// [ValueProvider("TestNamespace.Constants")] public int myField; - /// public void Foo([ValueProvider("TestNamespace.Constants")] string str) { } - /// - /// public void Test() - /// { - /// Foo(/*try completion here*/);// - /// myField = /*try completion here*/ - /// } - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, - AllowMultiple = true)] - internal sealed class ValueProviderAttribute : Attribute + [NotNull] public string FormatParameterName { get; } + } + + /// + /// Indicates that the marked parameter is a message template where placeholders are to be replaced by the following arguments + /// in the order in which they appear. + /// + /// + /// void LogInfo([StructuredMessageTemplate]string message, params object[] args) { /* do something */ } + /// + /// void Foo() { + /// LogInfo("User created: {username}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] +internal sealed class StructuredMessageTemplateAttribute : Attribute {} + + /// + /// Use this annotation to specify a type that contains static or const fields + /// with values for the annotated property/field/parameter. + /// The specified type will be used to improve completion suggestions. + /// + /// + /// namespace TestNamespace + /// { + /// public class Constants + /// { + /// public static int INT_CONST = 1; + /// public const string STRING_CONST = "1"; + /// } + /// + /// public class Class1 + /// { + /// [ValueProvider("TestNamespace.Constants")] public int myField; + /// public void Foo([ValueProvider("TestNamespace.Constants")] string str) { } + /// + /// public void Test() + /// { + /// Foo(/*try completion here*/);// + /// myField = /*try completion here*/ + /// } + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, + AllowMultiple = true)] +internal sealed class ValueProviderAttribute : Attribute + { + public ValueProviderAttribute([NotNull] string name) { - public ValueProviderAttribute(string name) - { - Name = name; - } - - public string Name { get; } + Name = name; } - /// - /// Indicates that the function argument should be a string literal and match one - /// of the parameters of the caller function. For example, ReSharper annotates - /// the parameter of . - /// - /// - /// void Foo(string param) { - /// if (param == null) - /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol - /// } - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class InvokerParameterNameAttribute : Attribute { } - - /// - /// Indicates that the method is contained in a type that implements - /// System.ComponentModel.INotifyPropertyChanged interface and this method - /// is used to notify that some property value changed. - /// - /// - /// The method should be non-static and conform to one of the supported signatures: - /// - /// NotifyChanged(string) - /// NotifyChanged(params string[]) - /// NotifyChanged{T}(Expression{Func{T}}) - /// NotifyChanged{T,U}(Expression{Func{T,U}}) - /// SetProperty{T}(ref T, T, string) - /// - /// - /// - /// public class Foo : INotifyPropertyChanged { - /// public event PropertyChangedEventHandler PropertyChanged; - /// - /// [NotifyPropertyChangedInvocator] - /// protected virtual void NotifyChanged(string propertyName) { ... } - /// - /// string _name; - /// - /// public string Name { - /// get { return _name; } - /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } - /// } - /// } - /// - /// Examples of generated notifications: - /// - /// NotifyChanged("Property") - /// NotifyChanged(() => Property) - /// NotifyChanged((VM x) => x.Property) - /// SetProperty(ref myField, value, "Property") - /// - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + [NotNull] public string Name { get; } + } + + /// + /// Indicates that the integral value falls into the specified interval. + /// It's allowed to specify multiple non-intersecting intervals. + /// Values of interval boundaries are inclusive. + /// + /// + /// void Foo([ValueRange(0, 100)] int value) { + /// if (value == -1) { // Warning: Expression is always 'false' + /// ... + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | + AttributeTargets.Method | AttributeTargets.Delegate, + AllowMultiple = true)] +internal sealed class ValueRangeAttribute : Attribute + { + public object From { get; } + public object To { get; } + + public ValueRangeAttribute(long from, long to) { - public NotifyPropertyChangedInvocatorAttribute() { } - - public NotifyPropertyChangedInvocatorAttribute(string parameterName) - { - ParameterName = parameterName; - } - - public string? ParameterName { get; } + From = from; + To = to; } - /// - /// Describes dependency between method input and output. - /// - /// - ///

Function Definition Table syntax:

- /// - /// FDT ::= FDTRow [;FDTRow]* - /// FDTRow ::= Input => Output | Output <= Input - /// Input ::= ParameterName: Value [, Input]* - /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} - /// Value ::= true | false | null | notnull | canbenull - /// - /// If the method has a single input parameter, its name could be omitted.
- /// Using halt (or void/nothing, which is the same) for the method output - /// means that the method doesn't return normally (throws or terminates the process).
- /// Value canbenull is only applicable for output parameters.
- /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute - /// with rows separated by semicolon. There is no notion of order rows, all rows are checked - /// for applicability and applied per each program state tracked by the analysis engine.
- ///
- /// - /// - /// [ContractAnnotation("=> halt")] - /// public void TerminationMethod() - /// - /// - /// [ContractAnnotation("null <= param:null")] // reverse condition syntax - /// public string GetName(string surname) - /// - /// - /// [ContractAnnotation("s:null => true")] - /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() - /// - /// - /// // A method that returns null if the parameter is null, - /// // and not null if the parameter is not null - /// [ContractAnnotation("null => null; notnull => notnull")] - /// public object Transform(object data) - /// - /// - /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] - /// public bool TryParse(string s, out Person result) - /// - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - internal sealed class ContractAnnotationAttribute : Attribute + public ValueRangeAttribute(ulong from, ulong to) { - public ContractAnnotationAttribute(string contract) - : this(contract, false) { } - - public ContractAnnotationAttribute(string contract, bool forceFullStates) - { - Contract = contract; - ForceFullStates = forceFullStates; - } - - public string Contract { get; } - - public bool ForceFullStates { get; } + From = from; + To = to; } - /// - /// Indicates whether the marked element should be localized. - /// - /// - /// [LocalizationRequiredAttribute(true)] - /// class Foo { - /// string str = "my string"; // Warning: Localizable string - /// } - /// - [AttributeUsage(AttributeTargets.All)] - internal sealed class LocalizationRequiredAttribute : Attribute + public ValueRangeAttribute(long value) { - public LocalizationRequiredAttribute() : this(true) { } - - public LocalizationRequiredAttribute(bool required) - { - Required = required; - } - - public bool Required { get; } + From = To = value; } - /// - /// Indicates that the value of the marked type (or its derivatives) - /// cannot be compared using '==' or '!=' operators and Equals() - /// should be used instead. However, using '==' or '!=' for comparison - /// with null is always permitted. - /// - /// - /// [CannotApplyEqualityOperator] - /// class NoEquality { } - /// - /// class UsesNoEquality { - /// void Test() { - /// var ca1 = new NoEquality(); - /// var ca2 = new NoEquality(); - /// if (ca1 != null) { // OK - /// bool condition = ca1 == ca2; // Warning - /// } - /// } - /// } - /// - [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] - internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { } - - /// - /// When applied to a target attribute, specifies a requirement for any type marked - /// with the target attribute to implement or inherit specific type or types. - /// - /// - /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement - /// class ComponentAttribute : Attribute { } - /// - /// [Component] // ComponentAttribute requires implementing IComponent interface - /// class MyComponent : IComponent { } - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - [BaseTypeRequired(typeof(Attribute))] - internal sealed class BaseTypeRequiredAttribute : Attribute + public ValueRangeAttribute(ulong value) { - public BaseTypeRequiredAttribute(Type baseType) - { - BaseType = baseType; - } - - public Type BaseType { get; } + From = To = value; } - - /// - /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), - /// so this symbol will not be reported as unused (as well as by other usage inspections). - /// - [AttributeUsage(AttributeTargets.All, Inherited = false)] - internal sealed class UsedImplicitlyAttribute : Attribute + } + + /// + /// Indicates that the integral value never falls below zero. + /// + /// + /// void Foo([NonNegativeValue] int value) { + /// if (value == -1) { // Warning: Expression is always 'false' + /// ... + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | + AttributeTargets.Method | AttributeTargets.Delegate)] +internal sealed class NonNegativeValueAttribute : Attribute { } + + /// + /// Indicates that the function argument should be a string literal and match + /// one of the parameters of the caller function. This annotation is used for parameters + /// like 'string paramName' parameter of the constructor. + /// + /// + /// void Foo(string param) { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] +internal sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// System.ComponentModel.INotifyPropertyChanged interface and this method + /// is used to notify that some property value changed. + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// public class Foo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) { ... } + /// + /// string _name; + /// + /// public string Name { + /// get { return _name; } + /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method)] +internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) { - public UsedImplicitlyAttribute() - : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } - - public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) - : this(useKindFlags, ImplicitUseTargetFlags.Default) { } - - public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) - : this(ImplicitUseKindFlags.Default, targetFlags) { } + ParameterName = parameterName; + } - public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) - { - UseKindFlags = useKindFlags; - TargetFlags = targetFlags; - } + [CanBeNull] public string ParameterName { get; } + } + + /// + /// Describes dependence between method input and output. + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If the method has a single input parameter, its name could be omitted.
+ /// Using halt (or void/nothing, which is the same) for the method output + /// means that the method doesn't return normally (throws or terminates the process).
+ /// Value canbenull is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute + /// with rows separated by the semicolon. There is no notion of order rows, all rows are checked + /// for applicability and applied per each program state tracked by the analysis engine.
+ ///
+ /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("null <= param:null")] // reverse condition syntax + /// public string GetName(string surname) + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() + /// + /// + /// // A method that returns null if the parameter is null, + /// // and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] + /// public bool TryParse(string s, out Person result) + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] +internal sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string contract) + : this(contract, false) { } + + public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } - public ImplicitUseKindFlags UseKindFlags { get; } + [NotNull] public string Contract { get; } + + public bool ForceFullStates { get; } + } + + /// + /// Indicates whether the marked element should be localized. + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// class Foo { + /// string str = "my string"; // Warning: Localizable string + /// } + /// + [AttributeUsage(AttributeTargets.All)] +internal sealed class LocalizationRequiredAttribute : Attribute + { + public LocalizationRequiredAttribute() : this(true) { } + + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } - public ImplicitUseTargetFlags TargetFlags { get; } + public bool Required { get; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() + /// should be used instead. However, using '==' or '!=' for comparison + /// with null is always permitted. + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality { } + /// + /// class UsesNoEquality { + /// void Test() { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// if (ca1 != null) { // OK + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] +internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked + /// with the target attribute to implement or inherit the specific type or types. + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// class ComponentAttribute : Attribute { } + /// + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// class MyComponent : IComponent { } + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [BaseTypeRequired(typeof(Attribute))] +internal sealed class BaseTypeRequiredAttribute : Attribute + { + public BaseTypeRequiredAttribute([NotNull] Type baseType) + { + BaseType = baseType; } - /// - /// Can be applied to attributes, type parameters, and parameters of a type assignable from . - /// When applied to an attribute, the decorated attribute behaves the same as . - /// When applied to a type parameter or to a parameter of type , indicates that the corresponding type - /// is used implicitly. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter | AttributeTargets.Parameter)] - internal sealed class MeansImplicitUseAttribute : Attribute + [NotNull] public Type BaseType { get; } + } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will be ignored by usage-checking inspections.
+ /// You can use and + /// to configure how this attribute is applied. + ///
+ /// + /// [UsedImplicitly] + /// public class TypeConverter {} + /// + /// public class SummaryData + /// { + /// [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] + /// public SummaryData() {} + /// } + /// + /// [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.Default)] + /// public interface IService {} + /// + [AttributeUsage(AttributeTargets.All)] +internal sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) { - public MeansImplicitUseAttribute() - : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } - public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) - : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + public ImplicitUseKindFlags UseKindFlags { get; } - public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) - : this(ImplicitUseKindFlags.Default, targetFlags) { } + public ImplicitUseTargetFlags TargetFlags { get; } + } - public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) - { - UseKindFlags = useKindFlags; - TargetFlags = targetFlags; - } + /// + /// Can be applied to attributes, type parameters, and parameters of a type assignable from . + /// When applied to an attribute, the decorated attribute behaves the same as . + /// When applied to a type parameter or to a parameter of type , + /// indicates that the corresponding type is used implicitly. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter | AttributeTargets.Parameter)] +internal sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } - [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; } + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } - [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; } - } + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } - /// - /// Specify the details of implicitly used symbol when it is marked - /// with or . - /// - [Flags] - internal enum ImplicitUseKindFlags + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) { - Default = Access | Assign | InstantiatedWithFixedConstructorSignature, - - /// Only entity marked with attribute considered used. - Access = 1, - - /// Indicates implicit assignment to a member. - Assign = 2, - - /// - /// Indicates implicit instantiation of a type with fixed constructor signature. - /// That means any unused constructor parameters won't be reported as such. - /// - InstantiatedWithFixedConstructorSignature = 4, - - /// Indicates implicit instantiation of a type. - InstantiatedNoFixedConstructorSignature = 8, + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; } + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; } + + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; } + } + + /// + /// Specifies the details of an implicitly used symbol when it is marked + /// with or . + /// + [Flags] +internal enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, /// - /// Specify what is considered to be used implicitly when marked - /// with or . + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. /// - [Flags] - internal enum ImplicitUseTargetFlags + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specifies what is considered to be used implicitly when marked + /// with or . + /// + [Flags] +internal enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of the type marked with the attribute are considered used. + Members = 2, + /// Inherited entities are considered used. + WithInheritors = 4, + /// Entity marked with the attribute and all its members considered used. + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available APIs, + /// which should not be removed and so is treated as used. + /// + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + [AttributeUsage(AttributeTargets.All, Inherited = false)] +internal sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + + public PublicAPIAttribute([NotNull] string comment) { - Default = Itself, - Itself = 1, - - /// Members of entity marked with attribute are considered used. - Members = 2, - - /// Entity marked with attribute and all its members considered used. - WithMembers = Itself | Members + Comment = comment; } + [CanBeNull] public string Comment { get; } + } + + /// + /// Tells the code analysis engine if the parameter is completely handled when the invoked method is on stack. + /// If the parameter is a delegate, indicates that the delegate can only be invoked during method execution + /// (the delegate can be invoked zero or multiple times, but not stored to some field and invoked later, + /// when the containing method is no longer on the execution stack). + /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. + /// If is true, the attribute will only take effect if the method invocation is located under the 'await' expression. + /// + [AttributeUsage(AttributeTargets.Parameter)] +internal sealed class InstantHandleAttribute : Attribute + { /// - /// This attribute is intended to mark publicly available API - /// which should not be removed and so is treated as used. + /// Require the method invocation to be used under the 'await' expression for this attribute to take effect on the code analysis engine. + /// Can be used for delegate/enumerable parameters of 'async' methods. /// - [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] - [AttributeUsage(AttributeTargets.All, Inherited = false)] - internal sealed class PublicAPIAttribute : Attribute + public bool RequireAwait { get; set; } + } + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as System.Diagnostics.Contracts.PureAttribute. + /// + /// + /// [Pure] int Multiply(int x, int y) => x * y; + /// + /// void M() { + /// Multiply(123, 42); // Warning: Return value of pure method is not used + /// } + /// + [AttributeUsage(AttributeTargets.Method)] +internal sealed class PureAttribute : Attribute { } + + /// + /// Indicates that the return value of the method invocation must be used. + /// + /// + /// Methods decorated with this attribute (in contrast to pure methods) might change state, + /// but make no sense without using their return value.
+ /// Similarly to , this attribute + /// will help to detect usages of the method when the return value is not used. + /// Optionally, you can specify a message to use when showing warnings, e.g. + /// [MustUseReturnValue("Use the return value to...")]. + ///
+ [AttributeUsage(AttributeTargets.Method)] +internal sealed class MustUseReturnValueAttribute : Attribute + { + public MustUseReturnValueAttribute() { } + + public MustUseReturnValueAttribute([NotNull] string justification) { - public PublicAPIAttribute() { } - - public PublicAPIAttribute(string comment) - { - Comment = comment; - } - - public string? Comment { get; } + Justification = justification; } - /// - /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. - /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. - /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class InstantHandleAttribute : Attribute { } - - /// - /// Indicates that a method does not make any observable state changes. - /// The same as System.Diagnostics.Contracts.PureAttribute. - /// - /// - /// [Pure] int Multiply(int x, int y) => x * y; - /// - /// void M() { - /// Multiply(123, 42); // Waring: Return value of pure method is not used - /// } - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class PureAttribute : Attribute { } - - /// - /// Indicates that the return value of the method invocation must be used. - /// - /// - /// Methods decorated with this attribute (in contrast to pure methods) might change state, - /// but make no sense without using their return value.
- /// Similarly to , this attribute - /// will help detecting usages of the method when the return value in not used. - /// Additionally, you can optionally specify a custom message, which will be used when showing warnings, e.g. - /// [MustUseReturnValue("Use the return value to...")]. - ///
- [AttributeUsage(AttributeTargets.Method)] - internal sealed class MustUseReturnValueAttribute : Attribute + [CanBeNull] public string Justification { get; } + } + + /// + /// This annotation allows to enforce allocation-less usage patterns of delegates for performance-critical APIs. + /// When this annotation is applied to the parameter of delegate type, the IDE checks the input argument of this parameter: + /// * When a lambda expression or anonymous method is passed as an argument, the IDE verifies that the passed closure + /// has no captures of the containing local variables and the compiler is able to cache the delegate instance + /// to avoid heap allocations. Otherwise a warning is produced. + /// * The IDE warns when the method name or local function name is passed as an argument as this always results + /// in heap allocation of the delegate instance. + /// + /// + /// In C# 9.0+ code, the IDE will also suggest to annotate the anonymous function with the 'static' modifier + /// to make use of the similar analysis provided by the language/compiler. + /// + [AttributeUsage(AttributeTargets.Parameter)] +internal sealed class RequireStaticDelegateAttribute : Attribute + { + public bool IsError { get; set; } + } + + /// + /// Indicates the type member or parameter of some type that should be used instead of all other ways + /// to get the value of that type. This annotation is useful when you have some "context" value evaluated + /// and stored somewhere, meaning that all other ways to get this value must be consolidated with the existing one. + /// + /// + /// class Foo { + /// [ProvidesContext] IBarService _barService = ...; + /// + /// void ProcessNode(INode node) { + /// DoSomething(node, node.GetGlobalServices().Bar); + /// // ^ Warning: use value of '_barService' field + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] +internal sealed class ProvidesContextAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + + public PathReferenceAttribute([NotNull, PathReference] string basePath) { - public MustUseReturnValueAttribute() { } - - public MustUseReturnValueAttribute(string justification) - { - Justification = justification; - } - - public string? Justification { get; } + BasePath = basePath; } + [CanBeNull] public string BasePath { get; } + } + + /// + /// An extension method marked with this attribute is processed by code completion + /// as a 'Source Template'. When the extension method is completed over some expression, its source code + /// is automatically expanded like a template at the call site. + /// + /// + /// Template method bodies can contain valid source code and/or special comments starting with '$'. + /// Text inside these comments is added as source code when the template is applied. Template parameters + /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. + /// Use the attribute to specify macros for parameters. + /// + /// + /// In this example, the 'forEach' method is a source template available over all values + /// of enumerable types, producing ordinary C# 'foreach' statement and placing the caret inside the block: + /// + /// [SourceTemplate] + /// public static void forEach<T>(this IEnumerable<T> xs) { + /// foreach (var x in xs) { + /// //$ $END$ + /// } + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method)] +internal sealed class SourceTemplateAttribute : Attribute { } + + /// + /// Allows specifying a macro for a parameter of a source template. + /// + /// + /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression + /// is defined in the property. When applied on a method, the target + /// template parameter is defined in the property. To apply the macro silently + /// for the parameter, set the property value to -1. + /// + /// + /// Applying the attribute on a source template method: + /// + /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] + /// public static void forEach<T>(this IEnumerable<T> collection) { + /// foreach (var item in collection) { + /// //$ $END$ + /// } + /// } + /// + /// Applying the attribute on a template method parameter: + /// + /// [SourceTemplate] + /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { + /// /*$ var $x$Id = "$newguid$" + x.ToString(); + /// x.DoSomething($x$Id); */ + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] +internal sealed class MacroAttribute : Attribute + { /// - /// Indicates the type member or parameter of some type, that should be used instead of all other ways - /// to get the value of that type. This annotation is useful when you have some "context" value evaluated - /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. + /// Allows specifying a macro that will be executed for a source template + /// parameter when the template is expanded. /// - /// - /// class Foo { - /// [ProvidesContext] IBarService _barService = ...; - /// - /// void ProcessNode(INode node) { - /// DoSomething(node, node.GetGlobalServices().Bar); - /// // ^ Warning: use value of '_barService' field - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] - internal sealed class ProvidesContextAttribute : Attribute { } + [CanBeNull] public string Expression { get; set; } /// - /// Indicates that a parameter is a path to a file or a folder within a web project. - /// Path can be relative or absolute, starting from web root (~). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class PathReferenceAttribute : Attribute - { - public PathReferenceAttribute() { } - - public PathReferenceAttribute([PathReference] string basePath) - { - BasePath = basePath; - } - - public string? BasePath { get; } - } - - /// - /// An extension method marked with this attribute is processed by code completion - /// as a 'Source Template'. When the extension method is completed over some expression, its source code - /// is automatically expanded like a template at call site. + /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. /// /// - /// Template method body can contain valid source code and/or special comments starting with '$'. - /// Text inside these comments is added as source code when the template is applied. Template parameters - /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. - /// Use the attribute to specify macros for parameters. + /// If the target parameter is used several times in the template, only one occurrence becomes editable; + /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, + /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. /// - /// - /// In this example, the 'forEach' method is a source template available over all values - /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: - /// - /// [SourceTemplate] - /// public static void forEach<T>(this IEnumerable<T> xs) { - /// foreach (var x in xs) { - /// //$ $END$ - /// } - /// } - /// - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class SourceTemplateAttribute : Attribute { } + public int Editable { get; set; } /// - /// Allows specifying a macro for a parameter of a source template. + /// Identifies the target parameter of a source template if the + /// is applied on a template method. /// - /// - /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression - /// is defined in the property. When applied on a method, the target - /// template parameter is defined in the property. To apply the macro silently - /// for the parameter, set the property value = -1. - /// - /// - /// Applying the attribute on a source template method: - /// - /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] - /// public static void forEach<T>(this IEnumerable<T> collection) { - /// foreach (var item in collection) { - /// //$ $END$ - /// } - /// } - /// - /// Applying the attribute on a template method parameter: - /// - /// [SourceTemplate] - /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { - /// /*$ var $x$Id = "$newguid$" + x.ToString(); - /// x.DoSomething($x$Id); */ - /// } - /// - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] - internal sealed class MacroAttribute : Attribute + [CanBeNull] public string Target { get; set; } + } + + /// + /// Indicates how a method, constructor invocation, or property access + /// over a collection type affects the contents of the collection. + /// When applied to a return value of a method, indicates if the returned collection + /// is created exclusively for the caller (CollectionAccessType.UpdatedContent) or + /// can be read/updated from outside (CollectionAccessType.Read | CollectionAccessType.UpdatedContent) + /// Use to specify the access type. + /// + /// + /// Using this attribute only makes sense if all collection methods are marked with this attribute. + /// + /// + /// public class MyStringCollection : List<string> + /// { + /// [CollectionAccess(CollectionAccessType.Read)] + /// public string GetFirstString() + /// { + /// return this.ElementAt(0); + /// } + /// } + /// class Test + /// { + /// public void Foo() + /// { + /// // Warning: Contents of the collection is never updated + /// var col = new MyStringCollection(); + /// string x = col.GetFirstString(); + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property | AttributeTargets.ReturnValue)] +internal sealed class CollectionAccessAttribute : Attribute + { + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) { - /// - /// Allows specifying a macro that will be executed for a source template - /// parameter when the template is expanded. - /// - public string? Expression { get; set; } - - /// - /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. - /// - /// - /// If the target parameter is used several times in the template, only one occurrence becomes editable; - /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, - /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. - /// - public int Editable { get; set; } - - /// - /// Identifies the target parameter of a source template if the - /// is applied on a template method. - /// - public string? Target { get; set; } + CollectionAccessType = collectionAccessType; } - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + public CollectionAccessType CollectionAccessType { get; } + } + + /// + /// Provides a value for the to define + /// how the collection method invocation affects the contents of the collection. + /// + [Flags] +internal enum CollectionAccessType + { + /// Method does not use or modify content of the collection. + None = 0, + /// Method only reads content of the collection but does not modify it. + Read = 1, + /// Method can change content of the collection but does not add new elements. + ModifyExistingContent = 2, + /// Method can add new elements to the collection. + UpdatedContent = ModifyExistingContent | 4 + } + + /// + /// Indicates that the marked method is an assertion method, i.e. it halts the control flow if + /// one of the conditions is satisfied. To set the condition, mark one of the parameters with + /// attribute. + /// + [AttributeUsage(AttributeTargets.Method)] +internal sealed class AssertionMethodAttribute : Attribute { } + + /// + /// Indicates the condition parameter of the assertion method. The method itself should be + /// marked by the attribute. The mandatory argument of + /// the attribute is the assertion type. + /// + [AttributeUsage(AttributeTargets.Parameter)] +internal sealed class AssertionConditionAttribute : Attribute + { + public AssertionConditionAttribute(AssertionConditionType conditionType) { - public AspMvcAreaMasterLocationFormatAttribute(string format) - { - Format = format; - } - - public string Format { get; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute - { - public AspMvcAreaPartialViewLocationFormatAttribute(string format) - { - Format = format; - } - - public string Format { get; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute - { - public AspMvcAreaViewLocationFormatAttribute(string format) - { - Format = format; - } - - public string Format { get; } + ConditionType = conditionType; } - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcMasterLocationFormatAttribute : Attribute + public AssertionConditionType ConditionType { get; } + } + + /// + /// Specifies the assertion type. If the assertion method argument satisfies the condition, + /// then the execution continues. Otherwise, execution is assumed to be halted. + /// +internal enum AssertionConditionType + { + /// Marked parameter should be evaluated to true. + IS_TRUE = 0, + /// Marked parameter should be evaluated to false. + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value. + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value. + IS_NOT_NULL = 3, + } + + /// + /// Indicates that the marked method unconditionally terminates control flow execution. + /// For example, it could unconditionally throw an exception. + /// + [Obsolete("Use [ContractAnnotation('=> halt')] instead")] + [AttributeUsage(AttributeTargets.Method)] +internal sealed class TerminatesProgramAttribute : Attribute { } + + /// + /// Indicates that the method is a pure LINQ method, with postponed enumeration (like Enumerable.Select, + /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters + /// of delegate type by analyzing LINQ method chains. + /// + [AttributeUsage(AttributeTargets.Method)] +internal sealed class LinqTunnelAttribute : Attribute { } + + /// + /// Indicates that IEnumerable passed as a parameter is not enumerated. + /// Use this annotation to suppress the 'Possible multiple enumeration of IEnumerable' inspection. + /// + /// + /// static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class + /// { + /// // custom check for null but no enumeration + /// } + /// + /// void Foo(IEnumerable<string> values) + /// { + /// ThrowIfNull(values, nameof(values)); + /// var x = values.ToList(); // No warnings about multiple enumeration + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] +internal sealed class NoEnumerationAttribute : Attribute { } + + /// + /// Indicates that the marked parameter, field, or property is a regular expression pattern. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class RegexPatternAttribute : Attribute { } + + /// + /// Language of injected code fragment inside marked by the string literal. + /// +internal enum InjectedLanguage + { + CSS, + HTML, + JAVASCRIPT, + JSON, + XML + } + + /// + /// Indicates that the marked parameter, field, or property is accepting a string literal + /// containing code fragments in a specified language. + /// + /// + /// void Foo([LanguageInjection(InjectedLanguage.CSS, Prefix = "body{", Suffix = "}")] string cssProps) + /// { + /// // cssProps should only contains a list of CSS properties + /// } + /// + /// + /// void Bar([LanguageInjection("json")] string json) + /// { + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class LanguageInjectionAttribute : Attribute + { + public LanguageInjectionAttribute(InjectedLanguage injectedLanguage) { - public AspMvcMasterLocationFormatAttribute(string format) - { - Format = format; - } - - public string Format { get; } + InjectedLanguage = injectedLanguage; } - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + public LanguageInjectionAttribute([NotNull] string injectedLanguage) { - public AspMvcPartialViewLocationFormatAttribute(string format) - { - Format = format; - } - - public string Format { get; } + InjectedLanguageName = injectedLanguage; } - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcViewLocationFormatAttribute : Attribute + /// Specifies a language of the injected code fragment. + public InjectedLanguage InjectedLanguage { get; } + + /// Specifies a language name of the injected code fragment. + [CanBeNull] public string InjectedLanguageName { get; } + + /// Specifies a string that "precedes" the injected string literal. + [CanBeNull] public string Prefix { get; set; } + + /// Specifies a string that "follows" the injected string literal. + [CanBeNull] public string Suffix { get; set; } + } + + /// + /// Prevents the Member Reordering feature from tossing members of the marked class. + /// + /// + /// The attribute must be mentioned in your member reordering patterns. + /// + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] +internal sealed class NoReorderAttribute : Attribute { } + + /// + /// + /// Defines the code search template using the Structural Search and Replace syntax. + /// It allows you to find and, if necessary, replace blocks of code that match a specific pattern. + /// Search and replace patterns consist of a textual part and placeholders. + /// Textural part must contain only identifiers allowed in the target language and will be matched exactly (white spaces, tabulation characters, and line breaks are ignored). + /// Placeholders allow matching variable parts of the target code blocks. + /// A placeholder has the following format: $placeholder_name$- where placeholder_name is an arbitrary identifier. + /// + /// + /// Available placeholders: + /// + /// $this$ - expression of containing type + /// $thisType$ - containing type + /// $member$ - current member placeholder + /// $qualifier$ - this placeholder is available in the replace pattern and can be used to insert a qualifier expression matched by the $member$ placeholder. + /// (Note that if $qualifier$ placeholder is used, then $member$ placeholder will match only qualified references) + /// $expression$ - expression of any type + /// $identifier$ - identifier placeholder + /// $args$ - any number of arguments + /// $arg$ - single argument + /// $arg1$ ... $arg10$ - single argument + /// $stmts$ - any number of statements + /// $stmt$ - single statement + /// $stmt1$ ... $stmt10$ - single statement + /// $name{Expression, 'Namespace.FooType'}$ - expression with 'Namespace.FooType' type + /// $expression{'Namespace.FooType'}$ - expression with 'Namespace.FooType' type + /// $name{Type, 'Namespace.FooType'}$ - 'Namespace.FooType' type + /// $type{'Namespace.FooType'}$ - 'Namespace.FooType' type + /// $statement{1,2}$ - 1 or 2 statements + /// + /// + /// + /// Note that you can also define your own placeholders of the supported types and specify arguments for each placeholder type. + /// This can be done using the following format: $name{type, arguments}$. Where 'name' - is the name of your placeholder, + /// 'type' - is the type of your placeholder (one of the following: Expression, Type, Identifier, Statement, Argument, Member), + /// 'arguments' - arguments list for your placeholder. Each placeholder type supports its own arguments, check examples below for more details. + /// The placeholder type may be omitted and determined from the placeholder name, if the name has one of the following prefixes: + /// + /// expr, expression - expression placeholder, e.g. $exprPlaceholder{}$, $expressionFoo{}$ + /// arg, argument - argument placeholder, e.g. $argPlaceholder{}$, $argumentFoo{}$ + /// ident, identifier - identifier placeholder, e.g. $identPlaceholder{}$, $identifierFoo{}$ + /// stmt, statement - statement placeholder, e.g. $stmtPlaceholder{}$, $statementFoo{}$ + /// type - type placeholder, e.g. $typePlaceholder{}$, $typeFoo{}$ + /// member - member placeholder, e.g. $memberPlaceholder{}$, $memberFoo{}$ + /// + /// + /// + /// Expression placeholder arguments: + /// + /// expressionType - string value in single quotes, specifies full type name to match (empty string by default) + /// exactType - boolean value, specifies if expression should have exact type match (false by default) + /// + /// Examples: + /// + /// $myExpr{Expression, 'Namespace.FooType', true}$ - defines expression placeholder, matching expressions of the 'Namespace.FooType' type with exact matching. + /// $myExpr{Expression, 'Namespace.FooType'}$ - defines expression placeholder, matching expressions of the 'Namespace.FooType' type or expressions which can be implicitly converted to 'Namespace.FooType'. + /// $myExpr{Expression}$ - defines expression placeholder, matching expressions of any type. + /// $exprFoo{'Namespace.FooType', true}$ - defines expression placeholder, matching expressions of the 'Namespace.FooType' type with exact matching. + /// + /// + /// + /// Type placeholder arguments: + /// + /// type - string value in single quotes, specifies full type name to match (empty string by default) + /// exactType - boolean value, specifies if expression should have exact type match (false by default) + /// + /// Examples: + /// + /// $myType{Type, 'Namespace.FooType', true}$ - defines type placeholder, matching 'Namespace.FooType' types with exact matching. + /// $myType{Type, 'Namespace.FooType'}$ - defines type placeholder, matching 'Namespace.FooType' types or types, which can be implicitly converted to 'Namespace.FooType'. + /// $myType{Type}$ - defines type placeholder, matching any type. + /// $typeFoo{'Namespace.FooType', true}$ - defines types placeholder, matching 'Namespace.FooType' types with exact matching. + /// + /// + /// + /// Identifier placeholder arguments: + /// + /// nameRegex - string value in single quotes, specifies regex to use for matching (empty string by default) + /// nameRegexCaseSensitive - boolean value, specifies if name regex is case sensitive (true by default) + /// type - string value in single quotes, specifies full type name to match (empty string by default) + /// exactType - boolean value, specifies if expression should have exact type match (false by default) + /// + /// Examples: + /// + /// $myIdentifier{Identifier, 'my.*', false, 'Namespace.FooType', true}$ - defines identifier placeholder, matching identifiers (ignoring case) starting with 'my' prefix with 'Namespace.FooType' type. + /// $myIdentifier{Identifier, 'my.*', true, 'Namespace.FooType', true}$ - defines identifier placeholder, matching identifiers (case sensitively) starting with 'my' prefix with 'Namespace.FooType' type. + /// $identFoo{'my.*'}$ - defines identifier placeholder, matching identifiers (case sensitively) starting with 'my' prefix. + /// + /// + /// + /// Statement placeholder arguments: + /// + /// minimalOccurrences - minimal number of statements to match (-1 by default) + /// maximalOccurrences - maximal number of statements to match (-1 by default) + /// + /// Examples: + /// + /// $myStmt{Statement, 1, 2}$ - defines statement placeholder, matching 1 or 2 statements. + /// $myStmt{Statement}$ - defines statement placeholder, matching any number of statements. + /// $stmtFoo{1, 2}$ - defines statement placeholder, matching 1 or 2 statements. + /// + /// + /// + /// Argument placeholder arguments: + /// + /// minimalOccurrences - minimal number of arguments to match (-1 by default) + /// maximalOccurrences - maximal number of arguments to match (-1 by default) + /// + /// Examples: + /// + /// $myArg{Argument, 1, 2}$ - defines argument placeholder, matching 1 or 2 arguments. + /// $myArg{Argument}$ - defines argument placeholder, matching any number of arguments. + /// $argFoo{1, 2}$ - defines argument placeholder, matching 1 or 2 arguments. + /// + /// + /// + /// Member placeholder arguments: + /// + /// docId - string value in single quotes, specifies XML documentation id of the member to match (empty by default) + /// + /// Examples: + /// + /// $myMember{Member, 'M:System.String.IsNullOrEmpty(System.String)'}$ - defines member placeholder, matching 'IsNullOrEmpty' member of the 'System.String' type. + /// $memberFoo{'M:System.String.IsNullOrEmpty(System.String)'}$ - defines member placeholder, matching 'IsNullOrEmpty' member of the 'System.String' type. + /// + /// + /// + /// For more information please refer to the Structural Search and Replace article. + /// + /// + [AttributeUsage( + AttributeTargets.Method + | AttributeTargets.Constructor + | AttributeTargets.Property + | AttributeTargets.Field + | AttributeTargets.Event + | AttributeTargets.Interface + | AttributeTargets.Class + | AttributeTargets.Struct + | AttributeTargets.Enum, + AllowMultiple = true, + Inherited = false)] +internal sealed class CodeTemplateAttribute : Attribute + { + public CodeTemplateAttribute(string searchTemplate) { - public AspMvcViewLocationFormatAttribute(string format) - { - Format = format; - } - - public string Format { get; } + SearchTemplate = searchTemplate; } /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC action. If applied to a method, the MVC action name is calculated - /// implicitly from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// Structural search pattern to use in the code template. + /// The pattern includes a textual part, which must contain only identifiers allowed in the target language, + /// and placeholders, which allow matching variable parts of the target code blocks. /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class AspMvcActionAttribute : Attribute - { - public AspMvcActionAttribute() { } - - public AspMvcActionAttribute(string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } - - public string? AnonymousProperty { get; } - } + public string SearchTemplate { get; } /// - /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC area. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// Message to show when the search pattern was found. + /// You can also prepend the message text with "Error:", "Warning:", "Suggestion:" or "Hint:" prefix to specify the pattern severity. + /// Code patterns with replace templates produce suggestions by default. + /// However, if a replace template is not provided, then warning severity will be used. /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class AspMvcAreaAttribute : Attribute - { - public AspMvcAreaAttribute() { } - - public AspMvcAreaAttribute(string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } - - public string? AnonymousProperty { get; } - } + public string Message { get; set; } /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is - /// an MVC controller. If applied to a method, the MVC controller name is calculated - /// implicitly from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). + /// Structural search replace pattern to use in code template replacement. /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class AspMvcControllerAttribute : Attribute - { - public AspMvcControllerAttribute() { } - - public AspMvcControllerAttribute(string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } - - public string? AnonymousProperty { get; } - } + public string ReplaceTemplate { get; set; } /// - /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC Master. Use this attribute - /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). + /// The replace message to show in the light bulb. /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class AspMvcMasterAttribute : Attribute { } + public string ReplaceMessage { get; set; } /// - /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC model type. Use this attribute - /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). + /// Apply code formatting after code replacement. /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcModelTypeAttribute : Attribute { } + public bool FormatAfterReplace { get; set; } = true; /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC - /// partial view. If applied to a method, the MVC partial view name is calculated implicitly - /// from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). + /// Whether similar code blocks should be matched. /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class AspMvcPartialViewAttribute : Attribute { } + public bool MatchSimilarConstructs { get; set; } /// - /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. + /// Automatically insert namespace import directives or remove qualifiers that become redundant after the template is applied. /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] - internal sealed class AspMvcSuppressViewErrorAttribute : Attribute { } + public bool ShortenReferences { get; set; } /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). + /// The string to use as a suppression key. + /// By default the following suppression key is used 'CodeTemplate_SomeType_SomeMember', + /// where 'SomeType' and 'SomeMember' are names of the associated containing type and member to which this attribute is applied. /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class AspMvcDisplayTemplateAttribute : Attribute { } + public string SuppressionKey { get; set; } + } - /// - /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC editor template. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class AspMvcEditorTemplateAttribute : Attribute { } - - /// - /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC template. - /// Use this attribute for custom wrappers similar to - /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class AspMvcTemplateAttribute : Attribute { } + #region ASP.NET - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly - /// from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Controller.View(Object). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class AspMvcViewAttribute : Attribute { } + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +internal sealed class AspChildControlTypeAttribute : Attribute + { + public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) + { + TagName = tagName; + ControlType = controlType; + } - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component name. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class AspMvcViewComponentAttribute : Attribute { } + [NotNull] public string TagName { get; } - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component view. If applied to a method, the MVC view component view name is default. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class AspMvcViewComponentViewAttribute : Attribute { } + [NotNull] public Type ControlType { get; } + } - /// - /// ASP.NET MVC attribute. When applied to a parameter of an attribute, - /// indicates that this parameter is an MVC action name. - /// - /// - /// [ActionName("Foo")] - /// public ActionResult Login(string returnUrl) { - /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK - /// return RedirectToAction("Bar"); // Error: Cannot resolve action - /// } - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] - internal sealed class AspMvcActionSelectorAttribute : Attribute { } - - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] - internal sealed class HtmlElementAttributesAttribute : Attribute - { - public HtmlElementAttributesAttribute() { } + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] +internal sealed class AspDataFieldAttribute : Attribute { } - public HtmlElementAttributesAttribute(string name) - { - Name = name; - } + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] +internal sealed class AspDataFieldsAttribute : Attribute { } - public string? Name { get; } - } + [AttributeUsage(AttributeTargets.Property)] +internal sealed class AspMethodPropertyAttribute : Attribute { } - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class HtmlAttributeValueAttribute : Attribute + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +internal sealed class AspRequiredAttributeAttribute : Attribute + { + public AspRequiredAttributeAttribute([NotNull] string attribute) { - public HtmlAttributeValueAttribute(string name) - { - Name = name; - } - - public string Name { get; } + Attribute = attribute; } - /// - /// Razor attribute. Indicates that the marked parameter or method is a Razor section. - /// Use this attribute for custom wrappers similar to - /// System.Web.WebPages.WebPageBase.RenderSection(String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class RazorSectionAttribute : Attribute { } + [NotNull] public string Attribute { get; } + } - /// - /// Indicates how method, constructor invocation, or property access - /// over collection type affects the contents of the collection. - /// Use to specify the access type. - /// - /// - /// Using this attribute only makes sense if all collection methods are marked with this attribute. - /// - /// - /// public class MyStringCollection : List<string> - /// { - /// [CollectionAccess(CollectionAccessType.Read)] - /// public string GetFirstString() - /// { - /// return this.ElementAt(0); - /// } - /// } - /// class Test - /// { - /// public void Foo() - /// { - /// // Warning: Contents of the collection is never updated - /// var col = new MyStringCollection(); - /// string x = col.GetFirstString(); - /// } - /// } - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] - internal sealed class CollectionAccessAttribute : Attribute - { - public CollectionAccessAttribute(CollectionAccessType collectionAccessType) - { - CollectionAccessType = collectionAccessType; - } + [AttributeUsage(AttributeTargets.Property)] +internal sealed class AspTypePropertyAttribute : Attribute + { + public bool CreateConstructorReferences { get; } - public CollectionAccessType CollectionAccessType { get; } - } - - /// - /// Provides a value for the to define - /// how the collection method invocation affects the contents of the collection. - /// - [Flags] - internal enum CollectionAccessType + public AspTypePropertyAttribute(bool createConstructorReferences) { - /// Method does not use or modify content of the collection. - None = 0, + CreateConstructorReferences = createConstructorReferences; + } + } - /// Method only reads content of the collection but does not modify it. - Read = 1, + #endregion - /// Method can change content of the collection but does not add new elements. - ModifyExistingContent = 2, + #region ASP.NET MVC - /// Method can add new elements to the collection. - UpdatedContent = ModifyExistingContent | 4 + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] +internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + { + public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; } - /// - /// Indicates that the marked method is assertion method, i.e. it halts the control flow if - /// one of the conditions is satisfied. To set the condition, mark one of the parameters with - /// attribute. - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class AssertionMethodAttribute : Attribute { } + [NotNull] public string Format { get; } + } - /// - /// Indicates the condition parameter of the assertion method. The method itself should be - /// marked by attribute. The mandatory argument of - /// the attribute is the assertion type. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AssertionConditionAttribute : Attribute + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] +internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute + { + public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) { - public AssertionConditionAttribute(AssertionConditionType conditionType) - { - ConditionType = conditionType; - } - - public AssertionConditionType ConditionType { get; } + Format = format; } - /// - /// Specifies assertion type. If the assertion method argument satisfies the condition, - /// then the execution continues. Otherwise, execution is assumed to be halted. - /// - internal enum AssertionConditionType - { - /// Marked parameter should be evaluated to true. - IS_TRUE = 0, - - /// Marked parameter should be evaluated to false. - IS_FALSE = 1, + [NotNull] public string Format { get; } + } - /// Marked parameter should be evaluated to null value. - IS_NULL = 2, - - /// Marked parameter should be evaluated to not null value. - IS_NOT_NULL = 3, + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] +internal sealed class AspMvcAreaViewComponentViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewComponentViewLocationFormatAttribute([NotNull] string format) + { + Format = format; } - /// - /// Indicates that the marked method unconditionally terminates control flow execution. - /// For example, it could unconditionally throw exception. - /// - [Obsolete("Use [ContractAnnotation('=> halt')] instead")] - [AttributeUsage(AttributeTargets.Method)] - internal sealed class TerminatesProgramAttribute : Attribute { } + [NotNull] public string Format { get; } + } - /// - /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, - /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters - /// of delegate type by analyzing LINQ method chains. - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class LinqTunnelAttribute : Attribute { } + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] +internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } - /// - /// Indicates that IEnumerable passed as a parameter is not enumerated. - /// Use this annotation to suppress the 'Possible multiple enumeration of IEnumerable' inspection. - /// - /// - /// static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class - /// { - /// // custom check for null but no enumeration - /// } - /// - /// void Foo(IEnumerable<string> values) - /// { - /// ThrowIfNull(values, nameof(values)); - /// var x = values.ToList(); // No warnings about multiple enumeration - /// } - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class NoEnumerationAttribute : Attribute { } + [NotNull] public string Format { get; } + } - /// - /// Indicates that the marked parameter is a regular expression pattern. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class RegexPatternAttribute : Attribute { } + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] +internal sealed class AspMvcMasterLocationFormatAttribute : Attribute + { + public AspMvcMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } - /// - /// Prevents the Member Reordering feature from tossing members of the marked class. - /// - /// - /// The attribute must be mentioned in your member reordering patterns. - /// - [AttributeUsage( - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] - internal sealed class NoReorderAttribute : Attribute { } + [NotNull] public string Format { get; } + } - /// - /// XAML attribute. Indicates the type that has ItemsSource property and should be treated - /// as ItemsControl-derived type, to enable inner items DataContext type resolve. - /// - [AttributeUsage(AttributeTargets.Class)] - internal sealed class XamlItemsControlAttribute : Attribute { } + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] +internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + { + public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } - /// - /// XAML attribute. Indicates the property of some BindingBase-derived type, that - /// is used to bind some item of ItemsControl-derived type. This annotation will - /// enable the DataContext type resolve for XAML bindings for such properties. - /// - /// - /// Property should have the tree ancestor of the ItemsControl type or - /// marked with the attribute. - /// - [AttributeUsage(AttributeTargets.Property)] - internal sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + [NotNull] public string Format { get; } + } - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - internal sealed class AspChildControlTypeAttribute : Attribute + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] +internal sealed class AspMvcViewComponentViewLocationFormatAttribute : Attribute + { + public AspMvcViewComponentViewLocationFormatAttribute([NotNull] string format) { - public AspChildControlTypeAttribute(string tagName, Type controlType) - { - TagName = tagName; - ControlType = controlType; - } + Format = format; + } - public string TagName { get; } + [NotNull] public string Format { get; } + } - public Type ControlType { get; } + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] +internal sealed class AspMvcViewLocationFormatAttribute : Attribute + { + public AspMvcViewLocationFormatAttribute([NotNull] string format) + { + Format = format; } - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] - internal sealed class AspDataFieldAttribute : Attribute { } + [NotNull] public string Format { get; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC action. If applied to a method, the MVC action name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcActionAttribute : Attribute + { + public AspMvcActionAttribute() { } + + public AspMvcActionAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] - internal sealed class AspDataFieldsAttribute : Attribute { } + [CanBeNull] public string AnonymousProperty { get; } + } - [AttributeUsage(AttributeTargets.Property)] - internal sealed class AspMethodPropertyAttribute : Attribute { } + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC area. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcAreaAttribute : Attribute + { + public AspMvcAreaAttribute() { } - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - internal sealed class AspRequiredAttributeAttribute : Attribute + public AspMvcAreaAttribute([NotNull] string anonymousProperty) { - public AspRequiredAttributeAttribute(string attribute) - { - Attribute = attribute; - } - - public string Attribute { get; } + AnonymousProperty = anonymousProperty; } - [AttributeUsage(AttributeTargets.Property)] - internal sealed class AspTypePropertyAttribute : Attribute + [CanBeNull] public string AnonymousProperty { get; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is + /// an MVC controller. If applied to a method, the MVC controller name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcControllerAttribute : Attribute + { + public AspMvcControllerAttribute() { } + + public AspMvcControllerAttribute([NotNull] string anonymousProperty) { - public bool CreateConstructorReferences { get; } - - public AspTypePropertyAttribute(bool createConstructorReferences) - { - CreateConstructorReferences = createConstructorReferences; - } + AnonymousProperty = anonymousProperty; } - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorImportNamespaceAttribute : Attribute + [CanBeNull] public string AnonymousProperty { get; } + } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC Master. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC model type. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). + /// + [AttributeUsage(AttributeTargets.Parameter)] +internal sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC + /// partial view. If applied to a method, the MVC partial view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcPartialViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] +internal sealed class AspMvcSuppressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC template. + /// Use this attribute for custom wrappers similar to + /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Controller.View(Object). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component name. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcViewComponentAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component view. If applied to a method, the MVC view component view name is default. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcViewComponentViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name. + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] +internal sealed class AspMvcActionSelectorAttribute : Attribute { } + + #endregion + + #region ASP.NET Routing + + /// + /// Indicates that the marked parameter, field, or property is a route template. + /// + /// + /// This attribute allows IDE to recognize the use of web frameworks' route templates + /// to enable syntax highlighting, code completion, navigation, rename and other features in string literals. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class RouteTemplateAttribute : Attribute { } + + /// + /// Indicates that the marked type is custom route parameter constraint, + /// which is registered in the application's Startup with the name ConstraintName. + /// + /// + /// You can specify ProposedType if target constraint matches only route parameters of specific type, + /// it will allow IDE to create method's parameter from usage in route template + /// with specified type instead of default System.String + /// and check if constraint's proposed type conflicts with matched parameter's type. + /// + [AttributeUsage(AttributeTargets.Class)] +internal sealed class RouteParameterConstraintAttribute : Attribute + { + [NotNull] public string ConstraintName { get; } + [CanBeNull] public Type ProposedType { get; set; } + + public RouteParameterConstraintAttribute([NotNull] string constraintName) + { + ConstraintName = constraintName; + } + } + + /// + /// Indicates that the marked parameter, field, or property is an URI string. + /// + /// + /// This attribute enables code completion, navigation, renaming and other features + /// in URI string literals assigned to annotated parameters, fields, or properties. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class UriStringAttribute : Attribute + { + public UriStringAttribute() { } + + public UriStringAttribute(string httpVerb) { - public RazorImportNamespaceAttribute(string name) - { - Name = name; - } + HttpVerb = httpVerb; + } - public string Name { get; } + [CanBeNull] public string HttpVerb { get; } + } + + /// + /// Indicates that the marked method declares routing convention for ASP.NET. + /// + /// + /// The IDE will analyze all usages of methods marked with this attribute, + /// and will add all routes to completion, navigation, and other features over URI strings. + /// + [AttributeUsage(AttributeTargets.Method)] +internal sealed class AspRouteConventionAttribute : Attribute + { + public AspRouteConventionAttribute() { } + + public AspRouteConventionAttribute(string predefinedPattern) + { + PredefinedPattern = predefinedPattern; } - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorInjectionAttribute : Attribute + [CanBeNull] public string PredefinedPattern { get; } + } + + /// + /// Indicates that the marked method parameter contains default route values of routing convention for ASP.NET. + /// + [AttributeUsage(AttributeTargets.Parameter)] +internal sealed class AspDefaultRouteValuesAttribute : Attribute { } + + /// + /// Indicates that the marked method parameter contains constraints on route values of routing convention for ASP.NET. + /// + [AttributeUsage(AttributeTargets.Parameter)] +internal sealed class AspRouteValuesConstraintsAttribute : Attribute { } + + /// + /// Indicates that the marked parameter or property contains routing order provided by ASP.NET routing attribute. + /// + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] +internal sealed class AspRouteOrderAttribute : Attribute { } + + /// + /// Indicates that the marked parameter or property contains HTTP verbs provided by ASP.NET routing attribute. + /// + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] +internal sealed class AspRouteVerbsAttribute : Attribute { } + + /// + /// Indicates that the marked attribute is used for attribute routing in ASP.NET. + /// + /// + /// The IDE will analyze all usages of attributes marked with this attribute, + /// and will add all routes to completion, navigation and other features over URI strings. + /// + [AttributeUsage(AttributeTargets.Class)] +internal sealed class AspAttributeRoutingAttribute : Attribute + { + public string HttpVerb { get; set; } + } + + /// + /// Indicates that the marked method declares an ASP.NET Minimal API endpoint. + /// + /// + /// The IDE will analyze all usages of methods marked with this attribute, + /// and will add all routes to completion, navigation and other features over URI strings. + /// + [AttributeUsage(AttributeTargets.Method)] +internal sealed class AspMinimalApiDeclarationAttribute : Attribute + { + public string HttpVerb { get; set; } + } + + /// + /// Indicates that the marked method declares an ASP.NET Minimal API endpoints group. + /// + [AttributeUsage(AttributeTargets.Method)] +internal sealed class AspMinimalApiGroupAttribute : Attribute { } + + /// + /// Indicates that the marked parameter contains an ASP.NET Minimal API endpoint handler. + /// + [AttributeUsage(AttributeTargets.Parameter)] +internal sealed class AspMinimalApiHandlerAttribute : Attribute { } + + #endregion + + #region Razor + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] +internal sealed class HtmlElementAttributesAttribute : Attribute + { + public HtmlElementAttributesAttribute() { } + + public HtmlElementAttributesAttribute([NotNull] string name) { - public RazorInjectionAttribute(string type, string fieldName) - { - Type = type; - FieldName = fieldName; - } + Name = name; + } - public string Type { get; } + [CanBeNull] public string Name { get; } + } - public string FieldName { get; } + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class HtmlAttributeValueAttribute : Attribute + { + public HtmlAttributeValueAttribute([NotNull] string name) + { + Name = name; } - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorDirectiveAttribute : Attribute + [NotNull] public string Name { get; } + } + + /// + /// Razor attribute. Indicates that the marked parameter or method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// System.Web.WebPages.WebPageBase.RenderSection(String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] +internal sealed class RazorSectionAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +internal sealed class RazorImportNamespaceAttribute : Attribute + { + public RazorImportNamespaceAttribute([NotNull] string name) { - public RazorDirectiveAttribute(string directive) - { - Directive = directive; - } - - public string Directive { get; } + Name = name; } - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorPageBaseTypeAttribute : Attribute + [NotNull] public string Name { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +internal sealed class RazorInjectionAttribute : Attribute + { + public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) { - public RazorPageBaseTypeAttribute(string baseType) - { - BaseType = baseType; - } - - public RazorPageBaseTypeAttribute(string baseType, string pageName) - { - BaseType = baseType; - PageName = pageName; - } - - public string BaseType { get; } - public string? PageName { get; } + Type = type; + FieldName = fieldName; } - [AttributeUsage(AttributeTargets.Method)] - internal sealed class RazorHelperCommonAttribute : Attribute { } + [NotNull] public string Type { get; } - [AttributeUsage(AttributeTargets.Property)] - internal sealed class RazorLayoutAttribute : Attribute { } + [NotNull] public string FieldName { get; } + } - [AttributeUsage(AttributeTargets.Method)] - internal sealed class RazorWriteLiteralMethodAttribute : Attribute { } + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +internal sealed class RazorDirectiveAttribute : Attribute + { + public RazorDirectiveAttribute([NotNull] string directive) + { + Directive = directive; + } - [AttributeUsage(AttributeTargets.Method)] - internal sealed class RazorWriteMethodAttribute : Attribute { } + [NotNull] public string Directive { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +internal sealed class RazorPageBaseTypeAttribute : Attribute + { + public RazorPageBaseTypeAttribute([NotNull] string baseType) + { + BaseType = baseType; + } + public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) + { + BaseType = baseType; + PageName = pageName; + } + + [NotNull] public string BaseType { get; } + [CanBeNull] public string PageName { get; } + } + + [AttributeUsage(AttributeTargets.Method)] +internal sealed class RazorHelperCommonAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] +internal sealed class RazorLayoutAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] +internal sealed class RazorWriteLiteralMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] +internal sealed class RazorWriteMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter)] +internal sealed class RazorWriteMethodParameterAttribute : Attribute { } + + #endregion + + #region XAML + + /// + /// XAML attribute. Indicates the type that has an ItemsSource property and should be treated + /// as an ItemsControl-derived type, to enable inner items DataContext type resolution. + /// + [AttributeUsage(AttributeTargets.Class)] +internal sealed class XamlItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some BindingBase-derived type, that + /// is used to bind some item of an ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// The property should have the tree ancestor of the ItemsControl type, or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] +internal sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some Style-derived type that + /// is used to style items of an ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] +internal sealed class XamlItemStyleOfItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates that DependencyProperty has OneWay binding mode by default. + /// + /// + /// This attribute must be applied to DependencyProperty's CLR accessor property if it is present, or to a DependencyProperty descriptor field otherwise. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class XamlOneWayBindingModeByDefaultAttribute : Attribute { } + + /// + /// XAML attribute. Indicates that DependencyProperty has TwoWay binding mode by default. + /// + /// + /// This attribute must be applied to DependencyProperty's CLR accessor property if it is present, or to a DependencyProperty descriptor field otherwise. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class XamlTwoWayBindingModeByDefaultAttribute : Attribute { } + + #endregion + + #region Unit Testing + + /// + /// Specifies the subject being tested by a test class or a test method. + /// + /// + /// The can be applied to a test class or a test method to indicate what class + /// or interface the tests defined in them test. This information can be used by an IDE to provide better navigation + /// support or by test runners to group tests by subject and to provide better test reports. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)] +internal sealed class TestSubjectAttribute : Attribute + { + /// + /// Gets the type of the subject being tested. + /// + [NotNull] public Type Subject { get; } - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class RazorWriteMethodParameterAttribute : Attribute { } -} \ No newline at end of file + /// + /// Initializes a new instance of the class with the specified subject type. + /// + /// The type of the subject being tested. + public TestSubjectAttribute([NotNull] Type subject) + { + Subject = subject; + } + } + + /// + /// Signifies a generic argument as the test subject for a test class. + /// + /// + /// The can be applied to a generic parameter of a base test class to indicate that + /// the type passed as the argument is the class being tested. This information can be used by an IDE to provide better + /// navigation support or by test runners to group tests by subject and to provide better test reports. + /// + /// + /// public class BaseTestClass<[MeansTestSubject] T> + /// { + /// protected T Component { get; } + /// } + /// + /// public class CalculatorAdditionTests : BaseTestClass<Calculator> + /// { + /// [Test] + /// public void Should_add_two_numbers() + /// { + /// Assert.That(Component.Add(2, 3), Is.EqualTo(5)); + /// } + /// } + /// + [AttributeUsage(AttributeTargets.GenericParameter)] +internal sealed class MeansTestSubjectAttribute : Attribute { } + + #endregion +} From 3860e4a0ec9de82f5addc750633ac0f7b470e08b Mon Sep 17 00:00:00 2001 From: Alina Smirnova Date: Sun, 27 Aug 2023 17:38:30 +0200 Subject: [PATCH 11/15] Removed redundant check --- src/BenchmarkDotNet/Code/CodeGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet/Code/CodeGenerator.cs b/src/BenchmarkDotNet/Code/CodeGenerator.cs index 52ae0e5478..94eafa02f9 100644 --- a/src/BenchmarkDotNet/Code/CodeGenerator.cs +++ b/src/BenchmarkDotNet/Code/CodeGenerator.cs @@ -93,7 +93,7 @@ internal static string Generate(BuildPartition buildPartition) private static void AddNonEmptyUnique(HashSet items, string value) { - if (!string.IsNullOrEmpty(value) && !items.Contains(value)) + if (!string.IsNullOrEmpty(value)) items.Add(value); } From 2a8bab554774fbd1d100a148846332a28a717ca1 Mon Sep 17 00:00:00 2001 From: Alina Smirnova Date: Sun, 27 Aug 2023 17:18:35 +0200 Subject: [PATCH 12/15] Fixed nullability warnings for some files from BenchmarkDotNet project --- src/BenchmarkDotNet/Analysers/Conclusion.cs | 4 ++-- .../Analysers/ZeroMeasurementHelper.cs | 2 +- .../Attributes/Filters/AotFilterAttribute.cs | 2 +- .../Attributes/Jobs/SimpleJobAttribute.cs | 10 +++++----- .../Characteristics/CharacteristicObject.cs | 4 ++-- .../Characteristics/CharacteristicObject`1.cs | 2 +- .../Characteristics/Characteristic`1.cs | 4 ++-- src/BenchmarkDotNet/Code/ArrayParam.cs | 17 ++++++++++------- src/BenchmarkDotNet/Code/CodeGenerator.cs | 2 +- src/BenchmarkDotNet/Jobs/Job.cs | 10 +++++----- src/BenchmarkDotNet/Jobs/JobMode`1.cs | 2 +- 11 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/BenchmarkDotNet/Analysers/Conclusion.cs b/src/BenchmarkDotNet/Analysers/Conclusion.cs index 10a9f3eeb0..0916bf9beb 100644 --- a/src/BenchmarkDotNet/Analysers/Conclusion.cs +++ b/src/BenchmarkDotNet/Analysers/Conclusion.cs @@ -38,7 +38,7 @@ public static Conclusion CreateWarning(string analyserId, string message, Benchm public static Conclusion CreateError(string analyserId, string message, BenchmarkReport? report = null, bool mergeable = true) => new Conclusion(analyserId, ConclusionKind.Error, message, report, mergeable); - public bool Equals(Conclusion other) + public bool Equals(Conclusion? other) { if (ReferenceEquals(null, other)) return false; @@ -49,7 +49,7 @@ public bool Equals(Conclusion other) return string.Equals(AnalyserId, other.AnalyserId) && Kind == other.Kind && string.Equals(Message, other.Message); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; diff --git a/src/BenchmarkDotNet/Analysers/ZeroMeasurementHelper.cs b/src/BenchmarkDotNet/Analysers/ZeroMeasurementHelper.cs index d94087239c..5e3f650167 100644 --- a/src/BenchmarkDotNet/Analysers/ZeroMeasurementHelper.cs +++ b/src/BenchmarkDotNet/Analysers/ZeroMeasurementHelper.cs @@ -20,7 +20,7 @@ public static bool CheckZeroMeasurementOneSample(double[] results, double thresh /// Checks distribution against Zero Measurement hypothesis in case of two samples ///
/// True if measurement is ZeroMeasurement - public static bool CheckZeroMeasurementTwoSamples(double[] workload, double[] overhead, Threshold threshold = null) + public static bool CheckZeroMeasurementTwoSamples(double[] workload, double[] overhead, Threshold? threshold = null) { if (workload.Length < 3 || overhead.Length < 3) return false; diff --git a/src/BenchmarkDotNet/Attributes/Filters/AotFilterAttribute.cs b/src/BenchmarkDotNet/Attributes/Filters/AotFilterAttribute.cs index 846fef2b8e..da0f610714 100644 --- a/src/BenchmarkDotNet/Attributes/Filters/AotFilterAttribute.cs +++ b/src/BenchmarkDotNet/Attributes/Filters/AotFilterAttribute.cs @@ -4,7 +4,7 @@ namespace BenchmarkDotNet.Attributes.Filters { public class AotFilterAttribute : FilterConfigBaseAttribute { - public AotFilterAttribute(string reason = null) + public AotFilterAttribute(string? reason = null) : base(new SimpleFilter(benchmark => !benchmark.GetRuntime().IsAOT)) { } diff --git a/src/BenchmarkDotNet/Attributes/Jobs/SimpleJobAttribute.cs b/src/BenchmarkDotNet/Attributes/Jobs/SimpleJobAttribute.cs index e0b80cf482..7a7052483c 100644 --- a/src/BenchmarkDotNet/Attributes/Jobs/SimpleJobAttribute.cs +++ b/src/BenchmarkDotNet/Attributes/Jobs/SimpleJobAttribute.cs @@ -18,7 +18,7 @@ public SimpleJobAttribute( int warmupCount = DefaultValue, int iterationCount = DefaultValue, int invocationCount = DefaultValue, - string id = null, + string? id = null, bool baseline = false ) : base(CreateJob(id, launchCount, warmupCount, iterationCount, invocationCount, null, baseline)) { } @@ -29,7 +29,7 @@ public SimpleJobAttribute( int warmupCount = DefaultValue, int iterationCount = DefaultValue, int invocationCount = DefaultValue, - string id = null, + string? id = null, bool baseline = false ) : base(CreateJob(id, launchCount, warmupCount, iterationCount, invocationCount, runStrategy, baseline)) { } @@ -40,7 +40,7 @@ public SimpleJobAttribute( int warmupCount = DefaultValue, int iterationCount = DefaultValue, int invocationCount = DefaultValue, - string id = null, + string? id = null, bool baseline = false ) : base(CreateJob(id, launchCount, warmupCount, iterationCount, invocationCount, null, baseline, runtimeMoniker)) { } @@ -52,11 +52,11 @@ public SimpleJobAttribute( int warmupCount = DefaultValue, int iterationCount = DefaultValue, int invocationCount = DefaultValue, - string id = null, + string? id = null, bool baseline = false ) : base(CreateJob(id, launchCount, warmupCount, iterationCount, invocationCount, runStrategy, baseline, runtimeMoniker)) { } - private static Job CreateJob(string id, int launchCount, int warmupCount, int iterationCount, int invocationCount, RunStrategy? runStrategy, + private static Job CreateJob(string? id, int launchCount, int warmupCount, int iterationCount, int invocationCount, RunStrategy? runStrategy, bool baseline, RuntimeMoniker runtimeMoniker = RuntimeMoniker.HostProcess) { var job = new Job(id); diff --git a/src/BenchmarkDotNet/Characteristics/CharacteristicObject.cs b/src/BenchmarkDotNet/Characteristics/CharacteristicObject.cs index b01f39e0fe..a6d7b37b81 100644 --- a/src/BenchmarkDotNet/Characteristics/CharacteristicObject.cs +++ b/src/BenchmarkDotNet/Characteristics/CharacteristicObject.cs @@ -44,7 +44,7 @@ protected CharacteristicObject() sharedValues = new Dictionary(); } - protected CharacteristicObject(string id) : this() + protected CharacteristicObject(string? id) : this() { if (!string.IsNullOrEmpty(id)) { @@ -98,7 +98,7 @@ private static void AssertIsAssignable(Characteristic characteristic, object val #region Properties - private CharacteristicObject Owner { get; set; } + private CharacteristicObject? Owner { get; set; } protected CharacteristicObject OwnerOrSelf => Owner ?? this; diff --git a/src/BenchmarkDotNet/Characteristics/CharacteristicObject`1.cs b/src/BenchmarkDotNet/Characteristics/CharacteristicObject`1.cs index a5959e1b62..e69167dc7b 100644 --- a/src/BenchmarkDotNet/Characteristics/CharacteristicObject`1.cs +++ b/src/BenchmarkDotNet/Characteristics/CharacteristicObject`1.cs @@ -8,7 +8,7 @@ public abstract class CharacteristicObject : CharacteristicObject { protected CharacteristicObject() { } - protected CharacteristicObject(string id) : base(id) { } + protected CharacteristicObject(string? id) : base(id) { } public new T Apply(CharacteristicObject other) => (T)ApplyCore(other); diff --git a/src/BenchmarkDotNet/Characteristics/Characteristic`1.cs b/src/BenchmarkDotNet/Characteristics/Characteristic`1.cs index 2cc61832a2..a5bc131817 100644 --- a/src/BenchmarkDotNet/Characteristics/Characteristic`1.cs +++ b/src/BenchmarkDotNet/Characteristics/Characteristic`1.cs @@ -8,7 +8,7 @@ public class Characteristic<[DynamicallyAccessedMembers(CharacteristicObject.Cha internal Characteristic( string id, Type declaringType, - Func resolver, + Func? resolver, T fallbackValue, bool ignoreOnApply, bool dontShowInSummary = false) @@ -18,7 +18,7 @@ internal Characteristic( FallbackValue = fallbackValue; } - private Func Resolver { get; } + private Func? Resolver { get; } public T FallbackValue { get; } diff --git a/src/BenchmarkDotNet/Code/ArrayParam.cs b/src/BenchmarkDotNet/Code/ArrayParam.cs index 1319067533..536f126f21 100644 --- a/src/BenchmarkDotNet/Code/ArrayParam.cs +++ b/src/BenchmarkDotNet/Code/ArrayParam.cs @@ -10,15 +10,15 @@ namespace BenchmarkDotNet.Code internal static class ArrayParam { public static string GetDisplayString(Array array) - => $"{array.GetType().GetElementType().GetDisplayName()}[{array.Length}]"; + => $"{array.GetType().GetElementType()?.GetDisplayName()}[{array.Length}]"; } public class ArrayParam : IParam { private readonly T[] array; - private readonly Func toSourceCode; + private readonly Func? toSourceCode; - private ArrayParam(T[] array, Func toSourceCode = null) + private ArrayParam(T[] array, Func? toSourceCode = null) { this.array = array; this.toSourceCode = toSourceCode; @@ -45,19 +45,22 @@ public string ToSourceCode() /// [PublicAPI] public static ArrayParam ForComplexTypes(T[] array, Func toSourceCode) => new ArrayParam(array, toSourceCode); - internal static IParam FromObject(object array) + internal static IParam? FromObject(object array) { var type = array.GetType(); if (!type.IsArray) throw new InvalidOperationException("The argument must be an array"); - if (!SourceCodeHelper.IsCompilationTimeConstant(type.GetElementType())) + var elementType = type.GetElementType(); + if (elementType == null) + throw new InvalidOperationException("Failed to determine type of array elements"); + if (!SourceCodeHelper.IsCompilationTimeConstant(elementType)) throw new InvalidOperationException("The argument must be an array of primitives"); - var arrayParamType = typeof(ArrayParam<>).MakeGenericType(type.GetElementType()); + var arrayParamType = typeof(ArrayParam<>).MakeGenericType(elementType); var methodInfo = arrayParamType.GetMethod(nameof(ForPrimitives), BindingFlags.Public | BindingFlags.Static) ?? throw new InvalidOperationException($"{nameof(ForPrimitives)} not found"); - return (IParam)methodInfo.Invoke(null, new[]{ array}); + return (IParam?)methodInfo.Invoke(null, new[]{ array}); } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Code/CodeGenerator.cs b/src/BenchmarkDotNet/Code/CodeGenerator.cs index 94eafa02f9..005564b77c 100644 --- a/src/BenchmarkDotNet/Code/CodeGenerator.cs +++ b/src/BenchmarkDotNet/Code/CodeGenerator.cs @@ -296,7 +296,7 @@ public SmartStringBuilder(string text) builder = new StringBuilder(text); } - public SmartStringBuilder Replace(string oldValue, string newValue) + public SmartStringBuilder Replace(string oldValue, string? newValue) { if (originalText.Contains(oldValue)) builder.Replace(oldValue, newValue); diff --git a/src/BenchmarkDotNet/Jobs/Job.cs b/src/BenchmarkDotNet/Jobs/Job.cs index eb73a1b6d7..0985b5e578 100644 --- a/src/BenchmarkDotNet/Jobs/Job.cs +++ b/src/BenchmarkDotNet/Jobs/Job.cs @@ -30,9 +30,9 @@ public sealed class Job : JobMode public static readonly Job InProcess = new Job(nameof(InProcess), InfrastructureMode.InProcess); public static readonly Job InProcessDontLogOutput = new Job(nameof(InProcessDontLogOutput), InfrastructureMode.InProcessDontLogOutput); - public Job() : this((string)null) { } + public Job() : this((string?)null) { } - public Job(string id) : base(id) + public Job(string? id) : base(id) { EnvironmentCharacteristic[this] = new EnvironmentMode(); RunCharacteristic[this] = new RunMode(); @@ -41,7 +41,7 @@ public Job(string id) : base(id) MetaCharacteristic[this] = new MetaMode(); } - public Job(CharacteristicObject other) : this((string)null, other) + public Job(CharacteristicObject other) : this((string?)null, other) { } @@ -49,12 +49,12 @@ public Job(params CharacteristicObject[] others) : this(null, others) { } - public Job(string id, CharacteristicObject other) : this(id) + public Job(string? id, CharacteristicObject other) : this(id) { Apply(other); } - public Job(string id, params CharacteristicObject[] others) : this(id) + public Job(string? id, params CharacteristicObject[] others) : this(id) { Apply(others); } diff --git a/src/BenchmarkDotNet/Jobs/JobMode`1.cs b/src/BenchmarkDotNet/Jobs/JobMode`1.cs index 5ffb4c07d8..61a51a8bd8 100644 --- a/src/BenchmarkDotNet/Jobs/JobMode`1.cs +++ b/src/BenchmarkDotNet/Jobs/JobMode`1.cs @@ -9,7 +9,7 @@ namespace BenchmarkDotNet.Jobs protected JobMode() { } - protected JobMode(string id) : base(id) { } + protected JobMode(string? id) : base(id) { } [PublicAPI] public Job Job => OwnerOrSelf as Job; } From b035d90c481bbbb8b8cd56daa5ca5730e2309797 Mon Sep 17 00:00:00 2001 From: AmirHossein Vahdanian Date: Fri, 1 Sep 2023 06:16:08 +0330 Subject: [PATCH 13/15] feat: add text justification style (#2410) * feat: add text justification style * fix: apply review changes * fix: remove extra space and set default justification to right for numeric columns * fix: use uniform justification for header columns * fix: use HeaderSeparator --- .../Exporters/MarkdownExporter.cs | 44 ++++++------- src/BenchmarkDotNet/Helpers/HashCode.cs | 20 +++++- src/BenchmarkDotNet/Reports/Summary.cs | 5 +- src/BenchmarkDotNet/Reports/SummaryStyle.cs | 41 ++++++++---- src/BenchmarkDotNet/Reports/SummaryTable.cs | 5 +- .../Reports/SummaryTableExtensions.cs | 37 +++++++++-- ...ceSummary_WithAllValuesOfBool.verified.txt | 6 +- ...ceSummary_WithAllValuesOfEnum.verified.txt | 8 +-- ...y_WithAllValuesOfNullableBool.verified.txt | 8 +-- ...y_WithAllValuesOfNullableEnum.verified.txt | 10 +-- ..._WithNotAllowedFlagsEnumError.verified.txt | 4 +- ...thNotAllowedNullableTypeError.verified.txt | 6 +- ...mmary_WithNotAllowedTypeError.verified.txt | 4 +- ...rifyTests.Exporters_Invariant.verified.txt | 36 +++++----- ...erVerifyTests.Exporters_en-US.verified.txt | 36 +++++----- ...erVerifyTests.Exporters_ru-RU.verified.txt | 36 +++++----- ...est_Escape_ParamsAndArguments.verified.txt | 14 ++-- ...rTest_Invalid_TwoJobBaselines.verified.txt | 10 +-- ...st_Invalid_TwoMethodBaselines.verified.txt | 6 +- ...rTest_JobBaseline_MethodsJobs.verified.txt | 14 ++-- ...JobBaseline_MethodsParamsJobs.verified.txt | 26 ++++---- ...erTest_MethodBaseline_Methods.verified.txt | 8 +-- ...st_MethodBaseline_MethodsJobs.verified.txt | 14 ++-- ..._MethodBaseline_MethodsParams.verified.txt | 14 ++-- ...hodBaseline_MethodsParamsJobs.verified.txt | 26 ++++---- ...MethodJobBaseline_MethodsJobs.verified.txt | 10 +-- ...JobBaseline_MethodsJobsParams.verified.txt | 18 ++--- ..._NoBaseline_MethodsParamsJobs.verified.txt | 26 ++++---- ..._MethodsParamsJobs_GroupByAll.verified.txt | 34 +++++----- ...odsParamsJobs_GroupByCategory.verified.txt | 34 +++++----- ..._MethodsParamsJobs_GroupByJob.verified.txt | 26 ++++---- ...thodsParamsJobs_GroupByMethod.verified.txt | 26 ++++---- ...thodsParamsJobs_GroupByParams.verified.txt | 26 ++++---- .../Mocks/MockFactory.cs | 66 +++++++++++-------- .../Reports/SummaryTableTests.cs | 22 +++++++ 35 files changed, 408 insertions(+), 318 deletions(-) diff --git a/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs b/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs index 096ae84b52..668396baa8 100644 --- a/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs +++ b/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs @@ -79,8 +79,8 @@ public enum MarkdownHighlightStrategy [PublicAPI] protected string CodeBlockStart = "```"; [PublicAPI] protected string CodeBlockEnd = "```"; [PublicAPI] protected MarkdownHighlightStrategy StartOfGroupHighlightStrategy = MarkdownHighlightStrategy.None; - [PublicAPI] protected string TableHeaderSeparator = " |"; - [PublicAPI] protected string TableColumnSeparator = " |"; + [PublicAPI] protected string TableHeaderSeparator = " | "; + [PublicAPI] protected string TableColumnSeparator = " | "; [PublicAPI] protected bool UseHeaderSeparatingRow = true; [PublicAPI] protected bool ColumnsStartWithSeparator; [PublicAPI] protected string BoldMarkupFormat = "**{0}**"; @@ -156,21 +156,17 @@ private void PrintTable(SummaryTable table, ILogger logger) logger.WriteLine(); } - if (ColumnsStartWithSeparator) - { - logger.WriteStatistic(TableHeaderSeparator.TrimStart()); - } + logger.WriteStatistic(ColumnsStartWithSeparator ? TableHeaderSeparator.TrimStart() : " "); table.PrintLine(table.FullHeader, logger, string.Empty, TableHeaderSeparator); if (UseHeaderSeparatingRow) { - if (ColumnsStartWithSeparator) - { - logger.WriteStatistic(TableHeaderSeparator.TrimStart()); - } + logger.WriteStatistic(ColumnsStartWithSeparator ? TableHeaderSeparator.TrimStart().TrimEnd() + "-" : "-"); logger.WriteLineStatistic(string.Join("", - table.Columns.Where(c => c.NeedToShow).Select(column => new string('-', column.Width) + GetJustificationIndicator(column.Justify) + "|"))); + table.Columns.Where(c => c.NeedToShow).Select(column => + new string('-', column.Width - 1) + GetHeaderSeparatorIndicator(column.OriginalColumn.IsNumeric) + + GetHeaderSeparatorColumnDivider(column.Index, table.ColumnCount)))); } int rowCounter = 0; @@ -181,8 +177,8 @@ private void PrintTable(SummaryTable table, ILogger logger) if (rowCounter > 0 && table.FullContentStartOfLogicalGroup[rowCounter] && table.SeparateLogicalGroups) { // Print logical separator - if (ColumnsStartWithSeparator) - logger.WriteStatistic(TableColumnSeparator.TrimStart()); + logger.WriteStatistic(ColumnsStartWithSeparator ? TableColumnSeparator.TrimStart() : " "); + table.PrintLine(separatorLine, logger, string.Empty, TableColumnSeparator, highlightRow, false, StartOfGroupHighlightStrategy, BoldMarkupFormat, false); } @@ -193,8 +189,8 @@ private void PrintTable(SummaryTable table, ILogger logger) highlightRow = !highlightRow; } - if (ColumnsStartWithSeparator) - logger.WriteStatistic(TableColumnSeparator.TrimStart()); + + logger.WriteStatistic(ColumnsStartWithSeparator ? TableColumnSeparator.TrimStart() : " "); table.PrintLine(line, logger, string.Empty, TableColumnSeparator, highlightRow, table.FullContentStartOfHighlightGroup[rowCounter], StartOfGroupHighlightStrategy, BoldMarkupFormat, EscapeHtml); @@ -202,17 +198,15 @@ private void PrintTable(SummaryTable table, ILogger logger) } } - private static string GetJustificationIndicator(SummaryTable.SummaryTableColumn.TextJustification textJustification) + private static string GetHeaderSeparatorIndicator(bool isNumeric) { - switch (textJustification) - { - case SummaryTable.SummaryTableColumn.TextJustification.Left: - return " "; - case SummaryTable.SummaryTableColumn.TextJustification.Right: - return ":"; - default: - return " "; - } + return isNumeric ? ":" : " "; + } + + private static string GetHeaderSeparatorColumnDivider(int columnIndex, int columnCount) + { + var isLastColumn = columnIndex != columnCount - 1; + return isLastColumn ? "|-" : "|"; } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Helpers/HashCode.cs b/src/BenchmarkDotNet/Helpers/HashCode.cs index 94a5858009..f7e5b948e1 100644 --- a/src/BenchmarkDotNet/Helpers/HashCode.cs +++ b/src/BenchmarkDotNet/Helpers/HashCode.cs @@ -109,6 +109,23 @@ public static int Combine(T1 value1, T2 value2, return hashCode; } + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, + T8 value8, T9 value9, T10 value10) + { + int hashCode = 0; + hashCode = Hash(hashCode, value1); + hashCode = Hash(hashCode, value2); + hashCode = Hash(hashCode, value3); + hashCode = Hash(hashCode, value4); + hashCode = Hash(hashCode, value5); + hashCode = Hash(hashCode, value6); + hashCode = Hash(hashCode, value7); + hashCode = Hash(hashCode, value8); + hashCode = Hash(hashCode, value9); + hashCode = Hash(hashCode, value10); + return hashCode; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Hash(int hashCode, T value) { @@ -128,7 +145,8 @@ private static int Hash(int hashCode, T value, IEqualityComparer comparer) } #pragma warning disable CS0809 // Obsolete member 'HashCode.GetHashCode()' overrides non-obsolete member 'object.GetHashCode()' - [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", + error: true)] [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() => throw new NotSupportedException(); diff --git a/src/BenchmarkDotNet/Reports/Summary.cs b/src/BenchmarkDotNet/Reports/Summary.cs index 782a2e6af9..7a9382f62d 100644 --- a/src/BenchmarkDotNet/Reports/Summary.cs +++ b/src/BenchmarkDotNet/Reports/Summary.cs @@ -47,7 +47,8 @@ public Summary( TimeSpan totalTime, CultureInfo cultureInfo, ImmutableArray validationErrors, - ImmutableArray columnHidingRules) + ImmutableArray columnHidingRules, + SummaryStyle summaryStyle = null) { Title = title; ResultsDirectoryPath = resultsDirectoryPath; @@ -64,7 +65,7 @@ public Summary( BenchmarksCases = Orderer.GetSummaryOrder(reports.Select(report => report.BenchmarkCase).ToImmutableArray(), this).ToImmutableArray(); // we sort it first Reports = BenchmarksCases.Select(b => ReportMap[b]).ToImmutableArray(); // we use sorted collection to re-create reports list BaseliningStrategy = BaseliningStrategy.Create(BenchmarksCases); - Style = GetConfiguredSummaryStyleOrDefaultOne(BenchmarksCases).WithCultureInfo(cultureInfo); + Style = (summaryStyle ?? GetConfiguredSummaryStyleOrDefaultOne(BenchmarksCases)).WithCultureInfo(cultureInfo); Table = GetTable(Style); AllRuntimes = BuildAllRuntimes(HostEnvironmentInfo, Reports); } diff --git a/src/BenchmarkDotNet/Reports/SummaryStyle.cs b/src/BenchmarkDotNet/Reports/SummaryStyle.cs index 461de108df..23451442df 100644 --- a/src/BenchmarkDotNet/Reports/SummaryStyle.cs +++ b/src/BenchmarkDotNet/Reports/SummaryStyle.cs @@ -3,6 +3,7 @@ using BenchmarkDotNet.Columns; using BenchmarkDotNet.Helpers; using Perfolizer.Horology; +using static BenchmarkDotNet.Reports.SummaryTable.SummaryTableColumn; // ReSharper disable MemberCanBePrivate.Global @@ -10,7 +11,9 @@ namespace BenchmarkDotNet.Reports { public class SummaryStyle : IEquatable { - public static readonly SummaryStyle Default = new SummaryStyle(DefaultCultureInfo.Instance, printUnitsInHeader: false, printUnitsInContent: true, printZeroValuesInContent: false, sizeUnit: null, timeUnit: null); + public static readonly SummaryStyle Default = new SummaryStyle(DefaultCultureInfo.Instance, printUnitsInHeader: false, printUnitsInContent: true, + printZeroValuesInContent: false, sizeUnit: null, timeUnit: null); + internal const int DefaultMaxParameterColumnWidth = 15 + 5; // 5 is for postfix " [15]" public bool PrintUnitsInHeader { get; } @@ -24,8 +27,12 @@ public class SummaryStyle : IEquatable public RatioStyle RatioStyle { get; } + public TextJustification TextColumnJustification { get; } + public TextJustification NumericColumnJustification { get; } + public SummaryStyle(CultureInfo? cultureInfo, bool printUnitsInHeader, SizeUnit sizeUnit, TimeUnit timeUnit, bool printUnitsInContent = true, - bool printZeroValuesInContent = false, int maxParameterColumnWidth = DefaultMaxParameterColumnWidth, RatioStyle ratioStyle = RatioStyle.Value) + bool printZeroValuesInContent = false, int maxParameterColumnWidth = DefaultMaxParameterColumnWidth, RatioStyle ratioStyle = RatioStyle.Value, + TextJustification textColumnJustification = TextJustification.Left, TextJustification numericColumnJustification = TextJustification.Right) { if (maxParameterColumnWidth < DefaultMaxParameterColumnWidth) throw new ArgumentOutOfRangeException(nameof(maxParameterColumnWidth), $"{DefaultMaxParameterColumnWidth} is the minimum."); @@ -35,29 +42,37 @@ public SummaryStyle(CultureInfo? cultureInfo, bool printUnitsInHeader, SizeUnit PrintUnitsInContent = printUnitsInContent; SizeUnit = sizeUnit; TimeUnit = timeUnit; - PrintZeroValuesInContent = printZeroValuesInContent; MaxParameterColumnWidth = maxParameterColumnWidth; RatioStyle = ratioStyle; CodeSizeUnit = SizeUnit.B; + PrintZeroValuesInContent = printZeroValuesInContent; + TextColumnJustification = textColumnJustification; + NumericColumnJustification = numericColumnJustification; } public SummaryStyle WithTimeUnit(TimeUnit timeUnit) - => new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, timeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, RatioStyle); + => new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, timeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, + RatioStyle, TextColumnJustification, NumericColumnJustification); public SummaryStyle WithSizeUnit(SizeUnit sizeUnit) - => new SummaryStyle(CultureInfo, PrintUnitsInHeader, sizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, RatioStyle); + => new SummaryStyle(CultureInfo, PrintUnitsInHeader, sizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, + RatioStyle, TextColumnJustification, NumericColumnJustification); public SummaryStyle WithZeroMetricValuesInContent() - => new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, printZeroValuesInContent: true, MaxParameterColumnWidth, RatioStyle); + => new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, printZeroValuesInContent: true, + MaxParameterColumnWidth, RatioStyle, TextColumnJustification, NumericColumnJustification); public SummaryStyle WithMaxParameterColumnWidth(int maxParameterColumnWidth) - => new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, maxParameterColumnWidth, RatioStyle); + => new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, maxParameterColumnWidth, + RatioStyle, TextColumnJustification, NumericColumnJustification); public SummaryStyle WithCultureInfo(CultureInfo cultureInfo) - => new SummaryStyle(cultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, RatioStyle); + => new SummaryStyle(cultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, + RatioStyle, TextColumnJustification, NumericColumnJustification); public SummaryStyle WithRatioStyle(RatioStyle ratioStyle) - => new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, ratioStyle); + => new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, + ratioStyle, TextColumnJustification, NumericColumnJustification); public bool Equals(SummaryStyle other) { @@ -73,7 +88,9 @@ public bool Equals(SummaryStyle other) && Equals(CodeSizeUnit, other.CodeSizeUnit) && Equals(TimeUnit, other.TimeUnit) && MaxParameterColumnWidth == other.MaxParameterColumnWidth - && RatioStyle == other.RatioStyle; + && RatioStyle == other.RatioStyle + && TextColumnJustification == other.TextColumnJustification + && NumericColumnJustification == other.NumericColumnJustification; } public override bool Equals(object obj) => obj is SummaryStyle summary && Equals(summary); @@ -87,7 +104,9 @@ public override int GetHashCode() => CodeSizeUnit, TimeUnit, MaxParameterColumnWidth, - RatioStyle); + RatioStyle, + TextColumnJustification, + NumericColumnJustification); public static bool operator ==(SummaryStyle left, SummaryStyle right) => Equals(left, right); diff --git a/src/BenchmarkDotNet/Reports/SummaryTable.cs b/src/BenchmarkDotNet/Reports/SummaryTable.cs index 59c4630bd3..9ab5e983fd 100644 --- a/src/BenchmarkDotNet/Reports/SummaryTable.cs +++ b/src/BenchmarkDotNet/Reports/SummaryTable.cs @@ -55,6 +55,7 @@ internal SummaryTable(Summary summary, SummaryStyle style = null) .Select(r => r.GcStats.GetBytesAllocatedPerOperation(r.BenchmarkCase).Value) .ToArray())); } + EffectiveSummaryStyle = style; var columns = summary.GetColumns(); ColumnCount = columns.Length; @@ -96,8 +97,6 @@ internal SummaryTable(Summary summary, SummaryStyle style = null) bool hide = summary.ColumnHidingRules.Any(rule => rule.NeedToHide(column)); Columns[i] = new SummaryTableColumn(this, i, column, hide); } - - EffectiveSummaryStyle = style; } public class SummaryTableColumn @@ -123,7 +122,7 @@ public SummaryTableColumn(SummaryTable table, int index, IColumn column, bool hi IsDefault = table.IsDefault[index]; OriginalColumn = column; - Justify = column.IsNumeric ? TextJustification.Right : TextJustification.Left; + Justify = column.IsNumeric ? table.EffectiveSummaryStyle.NumericColumnJustification : table.EffectiveSummaryStyle.TextColumnJustification; bool needToShow = column.AlwaysShow || Content.Distinct().Count() > 1; NeedToShow = !hide && needToShow; diff --git a/src/BenchmarkDotNet/Reports/SummaryTableExtensions.cs b/src/BenchmarkDotNet/Reports/SummaryTableExtensions.cs index 68419c3054..9c01c8e179 100644 --- a/src/BenchmarkDotNet/Reports/SummaryTableExtensions.cs +++ b/src/BenchmarkDotNet/Reports/SummaryTableExtensions.cs @@ -48,7 +48,8 @@ public static void PrintLine(this SummaryTable table, string[] line, ILogger log } public static void PrintLine(this SummaryTable table, string[] line, ILogger logger, string leftDel, string rightDel, - bool highlightRow, bool startOfGroup, MarkdownExporter.MarkdownHighlightStrategy startOfGroupHighlightStrategy, string boldMarkupFormat, bool escapeHtml) + bool highlightRow, bool startOfGroup, MarkdownExporter.MarkdownHighlightStrategy startOfGroupHighlightStrategy, string boldMarkupFormat, + bool escapeHtml) { for (int columnIndex = 0; columnIndex < table.ColumnCount; columnIndex++) { @@ -82,11 +83,23 @@ public static void PrintLine(this SummaryTable table, string[] line, ILogger log private static string BuildStandardText(SummaryTable table, string[] line, string leftDel, string rightDel, int columnIndex) { var buffer = GetClearBuffer(); + var isBuildingHeader = table.FullHeader[columnIndex] == line[columnIndex]; + var columnJustification = isBuildingHeader ? SummaryTable.SummaryTableColumn.TextJustification.Left : table.Columns[columnIndex].Justify; buffer.Append(leftDel); - PadLeft(table, line, leftDel, rightDel, columnIndex, buffer); + if (columnJustification == SummaryTable.SummaryTableColumn.TextJustification.Right) + { + AddPadding(table, line, leftDel, rightDel, columnIndex, buffer); + } + buffer.Append(line[columnIndex]); - buffer.Append(rightDel); + + if (columnJustification == SummaryTable.SummaryTableColumn.TextJustification.Left) + { + AddPadding(table, line, leftDel, rightDel, columnIndex, buffer); + } + var isLastColumn = columnIndex == table.ColumnCount - 1; + buffer.Append(isLastColumn ? rightDel.TrimEnd() : rightDel); return buffer.ToString(); } @@ -94,11 +107,23 @@ private static string BuildStandardText(SummaryTable table, string[] line, strin private static string BuildBoldText(SummaryTable table, string[] line, string leftDel, string rightDel, int columnIndex, string boldMarkupFormat) { var buffer = GetClearBuffer(); + var isBuildingHeader = table.FullHeader[columnIndex] == line[columnIndex]; + var columnJustification = isBuildingHeader ? SummaryTable.SummaryTableColumn.TextJustification.Left : table.Columns[columnIndex].Justify; buffer.Append(leftDel); - PadLeft(table, line, leftDel, rightDel, columnIndex, buffer); + if (columnJustification == SummaryTable.SummaryTableColumn.TextJustification.Right) + { + AddPadding(table, line, leftDel, rightDel, columnIndex, buffer); + } + buffer.AppendFormat(boldMarkupFormat, line[columnIndex]); - buffer.Append(rightDel); + + if (columnJustification == SummaryTable.SummaryTableColumn.TextJustification.Left) + { + AddPadding(table, line, leftDel, rightDel, columnIndex, buffer); + } + var isLastColumn = columnIndex == table.ColumnCount - 1; + buffer.Append(isLastColumn ? rightDel.TrimEnd() : rightDel); return buffer.ToString(); } @@ -116,7 +141,7 @@ private static StringBuilder GetClearBuffer() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void PadLeft(SummaryTable table, string[] line, string leftDel, string rightDel, int columnIndex, StringBuilder buffer) + private static void AddPadding(SummaryTable table, string[] line, string leftDel, string rightDel, int columnIndex, StringBuilder buffer) { const char space = ' '; const int extraWidth = 2; // " |".Length is not included in the column's Width diff --git a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfBool.verified.txt b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfBool.verified.txt index fba519229a..41554d0c7c 100644 --- a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfBool.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfBool.verified.txt @@ -7,9 +7,9 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC DefaultJob : extra output line - Method | ParamProperty | Mean | Error | StdDev | + Method | ParamProperty | Mean | Error | StdDev | ---------- |-------------- |---------:|--------:|--------:| - Benchmark | False | 102.0 ns | 6.09 ns | 1.58 ns | ^ - Benchmark | True | 202.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | False | 102.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | True | 202.0 ns | 6.09 ns | 1.58 ns | ^ Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfEnum.verified.txt b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfEnum.verified.txt index 0754264ed2..c1a301f759 100644 --- a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfEnum.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfEnum.verified.txt @@ -7,10 +7,10 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC DefaultJob : extra output line - Method | ParamProperty | Mean | Error | StdDev | + Method | ParamProperty | Mean | Error | StdDev | ---------- |-------------- |---------:|--------:|--------:| - Benchmark | A | 102.0 ns | 6.09 ns | 1.58 ns | ^ - Benchmark | B | 202.0 ns | 6.09 ns | 1.58 ns | ^ - Benchmark | C | 302.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | A | 102.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | B | 202.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | C | 302.0 ns | 6.09 ns | 1.58 ns | ^ Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfNullableBool.verified.txt b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfNullableBool.verified.txt index 23c73f72bf..61f4eeed4f 100644 --- a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfNullableBool.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfNullableBool.verified.txt @@ -7,10 +7,10 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC DefaultJob : extra output line - Method | ParamProperty | Mean | Error | StdDev | + Method | ParamProperty | Mean | Error | StdDev | ---------- |-------------- |---------:|--------:|--------:| - Benchmark | ? | 102.0 ns | 6.09 ns | 1.58 ns | ^ - Benchmark | False | 202.0 ns | 6.09 ns | 1.58 ns | ^ - Benchmark | True | 302.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | ? | 102.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | False | 202.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | True | 302.0 ns | 6.09 ns | 1.58 ns | ^ Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfNullableEnum.verified.txt b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfNullableEnum.verified.txt index a74fb9c3e4..0c1dd08fdc 100644 --- a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfNullableEnum.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfNullableEnum.verified.txt @@ -7,11 +7,11 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC DefaultJob : extra output line - Method | ParamProperty | Mean | Error | StdDev | + Method | ParamProperty | Mean | Error | StdDev | ---------- |-------------- |---------:|--------:|--------:| - Benchmark | ? | 102.0 ns | 6.09 ns | 1.58 ns | ^ - Benchmark | A | 202.0 ns | 6.09 ns | 1.58 ns | ^ - Benchmark | B | 302.0 ns | 6.09 ns | 1.58 ns | ^ - Benchmark | C | 402.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | ? | 102.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | A | 202.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | B | 302.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | C | 402.0 ns | 6.09 ns | 1.58 ns | ^ Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedFlagsEnumError.verified.txt b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedFlagsEnumError.verified.txt index de2ba47660..ba31d1bc3b 100644 --- a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedFlagsEnumError.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedFlagsEnumError.verified.txt @@ -7,9 +7,9 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC DefaultJob : extra output line - Method | ParamProperty | Mean | Error | StdDev | + Method | ParamProperty | Mean | Error | StdDev | ---------- |-------------- |---------:|--------:|--------:| - Benchmark | 0 | 102.0 ns | 6.09 ns | 1.58 ns | + Benchmark | 0 | 102.0 ns | 6.09 ns | 1.58 ns | Errors: 1 * Unable to use TestFlagsEnum with [ParamsAllValues], because it's flags enum. diff --git a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedNullableTypeError.verified.txt b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedNullableTypeError.verified.txt index 61c034243d..19566395d8 100644 --- a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedNullableTypeError.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedNullableTypeError.verified.txt @@ -7,10 +7,10 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC DefaultJob : extra output line - Method | ParamProperty | Mean | Error | StdDev | + Method | ParamProperty | Mean | Error | StdDev | ---------- |-------------- |---------:|--------:|--------:| - Benchmark | ? | 102.0 ns | 6.09 ns | 1.58 ns | ^ - Benchmark | 0 | 202.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | ? | 102.0 ns | 6.09 ns | 1.58 ns | ^ + Benchmark | 0 | 202.0 ns | 6.09 ns | 1.58 ns | ^ Errors: 1 * Type Int32 cannot be used with [ParamsAllValues], allowed types are: bool, enum types and nullable type for another allowed type. diff --git a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedTypeError.verified.txt b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedTypeError.verified.txt index 0e7587f03a..9a27385161 100644 --- a/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedTypeError.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithNotAllowedTypeError.verified.txt @@ -7,9 +7,9 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC DefaultJob : extra output line - Method | ParamProperty | Mean | Error | StdDev | + Method | ParamProperty | Mean | Error | StdDev | ---------- |-------------- |---------:|--------:|--------:| - Benchmark | 0 | 102.0 ns | 6.09 ns | 1.58 ns | + Benchmark | 0 | 102.0 ns | 6.09 ns | 1.58 ns | Errors: 1 * Type Int32 cannot be used with [ParamsAllValues], allowed types are: bool, enum types and nullable type for another allowed type. diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_Invariant.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_Invariant.verified.txt index 31d5cec733..cd3482c04f 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_Invariant.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_Invariant.verified.txt @@ -13,9 +13,9 @@ WarmupCount=15 .... [options="header"] |=== -| Method| Mean| Error| StdDev| P67 -| Foo| 1.000 ns| NA| 0.000 ns| 1.000 ns -| Bar| 1.000 ns| NA| 0.000 ns| 1.000 ns +|Method |Mean |Error |StdDev |P67 +|Foo | 1.000 ns| NA| 0.000 ns| 1.000 ns +|Bar | 1.000 ns| NA| 0.000 ns| 1.000 ns |=== ############################################ HtmlExporter @@ -410,10 +410,10 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 - Method | Mean | Error | StdDev | P67 | + Method | Mean | Error | StdDev | P67 | ------- |---------:|------:|---------:|---------:| - Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | - Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | + Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | + Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | ############################################ MarkdownExporter-atlassian ############################################ @@ -429,9 +429,9 @@ Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 {noformat} -||Method || Mean ||Error || StdDev || P67 || -| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | -| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | +||Method ||Mean ||Error ||StdDev ||P67 || +| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | +| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | ############################################ MarkdownExporter-console ############################################ @@ -445,10 +445,10 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 -| Method | Mean | Error | StdDev | P67 | +| Method | Mean | Error | StdDev | P67 | |------- |---------:|------:|---------:|---------:| -| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | -| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | +| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | +| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | ############################################ MarkdownExporter-github ############################################ @@ -464,10 +464,10 @@ Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 ``` -| Method | Mean | Error | StdDev | P67 | +| Method | Mean | Error | StdDev | P67 | |------- |---------:|------:|---------:|---------:| -| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | -| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | +| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | +| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | ############################################ MarkdownExporter-stackoverflow ############################################ @@ -481,10 +481,10 @@ MarkdownExporter-stackoverflow Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 - Method | Mean | Error | StdDev | P67 | + Method | Mean | Error | StdDev | P67 | ------- |---------:|------:|---------:|---------:| - Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | - Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | + Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | + Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | ############################################ PlainExporter ############################################ diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_en-US.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_en-US.verified.txt index 31d5cec733..cd3482c04f 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_en-US.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_en-US.verified.txt @@ -13,9 +13,9 @@ WarmupCount=15 .... [options="header"] |=== -| Method| Mean| Error| StdDev| P67 -| Foo| 1.000 ns| NA| 0.000 ns| 1.000 ns -| Bar| 1.000 ns| NA| 0.000 ns| 1.000 ns +|Method |Mean |Error |StdDev |P67 +|Foo | 1.000 ns| NA| 0.000 ns| 1.000 ns +|Bar | 1.000 ns| NA| 0.000 ns| 1.000 ns |=== ############################################ HtmlExporter @@ -410,10 +410,10 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 - Method | Mean | Error | StdDev | P67 | + Method | Mean | Error | StdDev | P67 | ------- |---------:|------:|---------:|---------:| - Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | - Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | + Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | + Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | ############################################ MarkdownExporter-atlassian ############################################ @@ -429,9 +429,9 @@ Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 {noformat} -||Method || Mean ||Error || StdDev || P67 || -| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | -| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | +||Method ||Mean ||Error ||StdDev ||P67 || +| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | +| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | ############################################ MarkdownExporter-console ############################################ @@ -445,10 +445,10 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 -| Method | Mean | Error | StdDev | P67 | +| Method | Mean | Error | StdDev | P67 | |------- |---------:|------:|---------:|---------:| -| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | -| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | +| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | +| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | ############################################ MarkdownExporter-github ############################################ @@ -464,10 +464,10 @@ Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 ``` -| Method | Mean | Error | StdDev | P67 | +| Method | Mean | Error | StdDev | P67 | |------- |---------:|------:|---------:|---------:| -| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | -| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | +| Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | +| Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | ############################################ MarkdownExporter-stackoverflow ############################################ @@ -481,10 +481,10 @@ MarkdownExporter-stackoverflow Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 - Method | Mean | Error | StdDev | P67 | + Method | Mean | Error | StdDev | P67 | ------- |---------:|------:|---------:|---------:| - Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | - Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | + Foo | 1.000 ns | NA | 0.000 ns | 1.000 ns | + Bar | 1.000 ns | NA | 0.000 ns | 1.000 ns | ############################################ PlainExporter ############################################ diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_ru-RU.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_ru-RU.verified.txt index 6071e78b4d..bbfaff1da8 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_ru-RU.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/CommonExporterVerifyTests.Exporters_ru-RU.verified.txt @@ -13,9 +13,9 @@ WarmupCount=15 .... [options="header"] |=== -| Method| Mean| Error| StdDev| P67 -| Foo| 1,000 ns| NA| 0,000 ns| 1,000 ns -| Bar| 1,000 ns| NA| 0,000 ns| 1,000 ns +|Method |Mean |Error |StdDev |P67 +|Foo | 1,000 ns| NA| 0,000 ns| 1,000 ns +|Bar | 1,000 ns| NA| 0,000 ns| 1,000 ns |=== ############################################ HtmlExporter @@ -410,10 +410,10 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 - Method | Mean | Error | StdDev | P67 | + Method | Mean | Error | StdDev | P67 | ------- |---------:|------:|---------:|---------:| - Foo | 1,000 ns | NA | 0,000 ns | 1,000 ns | - Bar | 1,000 ns | NA | 0,000 ns | 1,000 ns | + Foo | 1,000 ns | NA | 0,000 ns | 1,000 ns | + Bar | 1,000 ns | NA | 0,000 ns | 1,000 ns | ############################################ MarkdownExporter-atlassian ############################################ @@ -429,9 +429,9 @@ Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 {noformat} -||Method || Mean ||Error || StdDev || P67 || -| Foo | 1,000 ns | NA | 0,000 ns | 1,000 ns | -| Bar | 1,000 ns | NA | 0,000 ns | 1,000 ns | +||Method ||Mean ||Error ||StdDev ||P67 || +| Foo | 1,000 ns | NA | 0,000 ns | 1,000 ns | +| Bar | 1,000 ns | NA | 0,000 ns | 1,000 ns | ############################################ MarkdownExporter-console ############################################ @@ -445,10 +445,10 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 -| Method | Mean | Error | StdDev | P67 | +| Method | Mean | Error | StdDev | P67 | |------- |---------:|------:|---------:|---------:| -| Foo | 1,000 ns | NA | 0,000 ns | 1,000 ns | -| Bar | 1,000 ns | NA | 0,000 ns | 1,000 ns | +| Foo | 1,000 ns | NA | 0,000 ns | 1,000 ns | +| Bar | 1,000 ns | NA | 0,000 ns | 1,000 ns | ############################################ MarkdownExporter-github ############################################ @@ -464,10 +464,10 @@ Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 ``` -| Method | Mean | Error | StdDev | P67 | +| Method | Mean | Error | StdDev | P67 | |------- |---------:|------:|---------:|---------:| -| Foo | 1,000 ns | NA | 0,000 ns | 1,000 ns | -| Bar | 1,000 ns | NA | 0,000 ns | 1,000 ns | +| Foo | 1,000 ns | NA | 0,000 ns | 1,000 ns | +| Bar | 1,000 ns | NA | 0,000 ns | 1,000 ns | ############################################ MarkdownExporter-stackoverflow ############################################ @@ -481,10 +481,10 @@ MarkdownExporter-stackoverflow Job=LongRun IterationCount=100 LaunchCount=3 WarmupCount=15 - Method | Mean | Error | StdDev | P67 | + Method | Mean | Error | StdDev | P67 | ------- |---------:|------:|---------:|---------:| - Foo | 1,000 ns | NA | 0,000 ns | 1,000 ns | - Bar | 1,000 ns | NA | 0,000 ns | 1,000 ns | + Foo | 1,000 ns | NA | 0,000 ns | 1,000 ns | + Bar | 1,000 ns | NA | 0,000 ns | 1,000 ns | ############################################ PlainExporter ############################################ diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Escape_ParamsAndArguments.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Escape_ParamsAndArguments.verified.txt index 40bedf0647..78bde45ccb 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Escape_ParamsAndArguments.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Escape_ParamsAndArguments.verified.txt @@ -7,13 +7,13 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC DefaultJob : extra output line - Method | StringParam | charArg | Mean | Error | StdDev | + Method | StringParam | charArg | Mean | Error | StdDev | ------- |------------ |-------- |---------:|--------:|--------:| - Foo | \t | \t | 102.0 ns | 6.09 ns | 1.58 ns | ^ - Foo | \t | \n | 202.0 ns | 6.09 ns | 1.58 ns | ^ - Bar | \t | ? | 302.0 ns | 6.09 ns | 1.58 ns | ^ - Foo | \n | \t | 402.0 ns | 6.09 ns | 1.58 ns | ^ - Foo | \n | \n | 502.0 ns | 6.09 ns | 1.58 ns | ^ - Bar | \n | ? | 602.0 ns | 6.09 ns | 1.58 ns | ^ + Foo | \t | \t | 102.0 ns | 6.09 ns | 1.58 ns | ^ + Foo | \t | \n | 202.0 ns | 6.09 ns | 1.58 ns | ^ + Bar | \t | ? | 302.0 ns | 6.09 ns | 1.58 ns | ^ + Foo | \n | \t | 402.0 ns | 6.09 ns | 1.58 ns | ^ + Foo | \n | \n | 502.0 ns | 6.09 ns | 1.58 ns | ^ + Bar | \n | ? | 602.0 ns | 6.09 ns | 1.58 ns | ^ Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Invalid_TwoJobBaselines.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Invalid_TwoJobBaselines.verified.txt index c2ce72cecb..b690775469 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Invalid_TwoJobBaselines.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Invalid_TwoJobBaselines.verified.txt @@ -8,13 +8,13 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Job | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |----- |---------:|--------:|--------:|------:|--------:|-----:|---------------------------- |--------- | - Foo | Job1 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | Invalid_TwoJobBaselines.Foo | Yes | - Foo | Job2 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 2 | Invalid_TwoJobBaselines.Foo | Yes | + Foo | Job1 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | Invalid_TwoJobBaselines.Foo | Yes | + Foo | Job2 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 2 | Invalid_TwoJobBaselines.Foo | Yes | | | | | | | | | | | - Bar | Job1 | 202.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | Invalid_TwoJobBaselines.Bar | Yes | - Bar | Job2 | 402.0 ns | 6.09 ns | 1.58 ns | 1.99 | 0.01 | 2 | Invalid_TwoJobBaselines.Bar | Yes | + Bar | Job1 | 202.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | Invalid_TwoJobBaselines.Bar | Yes | + Bar | Job2 | 402.0 ns | 6.09 ns | 1.58 ns | 1.99 | 0.01 | 2 | Invalid_TwoJobBaselines.Bar | Yes | Errors: 2 * Only 1 job in a group can have "Baseline = true" applied to it, group Invalid_TwoJobBaselines.Foo in class Invalid_TwoJobBaselines has 2 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Invalid_TwoMethodBaselines.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Invalid_TwoMethodBaselines.verified.txt index c7782ee480..aaba6592c2 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Invalid_TwoMethodBaselines.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_Invalid_TwoMethodBaselines.verified.txt @@ -7,10 +7,10 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC DefaultJob : extra output line - Method | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |---------:|--------:|--------:|------:|--------:|-----:|------------- |--------- | - Foo | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | DefaultJob | Yes | - Bar | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | DefaultJob | Yes | + Foo | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | DefaultJob | Yes | + Bar | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | DefaultJob | Yes | Errors: 1 * Only 1 benchmark method in a group can have "Baseline = true" applied to it, group DefaultJob in class Invalid_TwoMethodBaselines has 2 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_JobBaseline_MethodsJobs.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_JobBaseline_MethodsJobs.verified.txt index af6d5917f2..e8793a9409 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_JobBaseline_MethodsJobs.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_JobBaseline_MethodsJobs.verified.txt @@ -8,15 +8,15 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Job | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |----- |---------:|--------:|--------:|------:|--------:|-----:|----------------------------- |--------- | - Base | Job1 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | JobBaseline_MethodsJobs.Base | Yes | - Base | Job2 | 402.0 ns | 6.09 ns | 1.58 ns | 3.94 | 0.05 | 2 | JobBaseline_MethodsJobs.Base | No | + Base | Job1 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | JobBaseline_MethodsJobs.Base | Yes | + Base | Job2 | 402.0 ns | 6.09 ns | 1.58 ns | 3.94 | 0.05 | 2 | JobBaseline_MethodsJobs.Base | No | | | | | | | | | | | - Foo | Job1 | 202.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | JobBaseline_MethodsJobs.Foo | Yes | - Foo | Job2 | 502.0 ns | 6.09 ns | 1.58 ns | 2.49 | 0.01 | 2 | JobBaseline_MethodsJobs.Foo | No | + Foo | Job1 | 202.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | JobBaseline_MethodsJobs.Foo | Yes | + Foo | Job2 | 502.0 ns | 6.09 ns | 1.58 ns | 2.49 | 0.01 | 2 | JobBaseline_MethodsJobs.Foo | No | | | | | | | | | | | - Bar | Job1 | 302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | JobBaseline_MethodsJobs.Bar | Yes | - Bar | Job2 | 602.0 ns | 6.09 ns | 1.58 ns | 1.99 | 0.01 | 2 | JobBaseline_MethodsJobs.Bar | No | + Bar | Job1 | 302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | JobBaseline_MethodsJobs.Bar | Yes | + Bar | Job2 | 602.0 ns | 6.09 ns | 1.58 ns | 1.99 | 0.01 | 2 | JobBaseline_MethodsJobs.Bar | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_JobBaseline_MethodsParamsJobs.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_JobBaseline_MethodsParamsJobs.verified.txt index cff1ed6d40..479192b41e 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_JobBaseline_MethodsParamsJobs.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_JobBaseline_MethodsParamsJobs.verified.txt @@ -8,24 +8,24 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Job | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |----- |------ |-----------:|--------:|--------:|------:|--------:|-----:|---------------------------------------------- |--------- | - Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-JobBaseline_MethodsParamsJobs.Base | Yes | ^ - Base | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | 3.94 | 0.05 | 2 | [Param=2]-JobBaseline_MethodsParamsJobs.Base | No | + Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-JobBaseline_MethodsParamsJobs.Base | Yes | ^ + Base | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | 3.94 | 0.05 | 2 | [Param=2]-JobBaseline_MethodsParamsJobs.Base | No | | | | | | | | | | | | - Foo | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-JobBaseline_MethodsParamsJobs.Foo | Yes | - Foo | Job2 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 2.49 | 0.01 | 2 | [Param=2]-JobBaseline_MethodsParamsJobs.Foo | No | + Foo | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-JobBaseline_MethodsParamsJobs.Foo | Yes | + Foo | Job2 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 2.49 | 0.01 | 2 | [Param=2]-JobBaseline_MethodsParamsJobs.Foo | No | | | | | | | | | | | | - Bar | Job1 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-JobBaseline_MethodsParamsJobs.Bar | Yes | - Bar | Job2 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 1.99 | 0.01 | 2 | [Param=2]-JobBaseline_MethodsParamsJobs.Bar | No | + Bar | Job1 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-JobBaseline_MethodsParamsJobs.Bar | Yes | + Bar | Job2 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 1.99 | 0.01 | 2 | [Param=2]-JobBaseline_MethodsParamsJobs.Bar | No | | | | | | | | | | | | - Base | Job1 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-JobBaseline_MethodsParamsJobs.Base | Yes | ^ - Base | Job2 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 1.43 | 0.00 | 2 | [Param=10]-JobBaseline_MethodsParamsJobs.Base | No | + Base | Job1 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-JobBaseline_MethodsParamsJobs.Base | Yes | ^ + Base | Job2 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 1.43 | 0.00 | 2 | [Param=10]-JobBaseline_MethodsParamsJobs.Base | No | | | | | | | | | | | | - Foo | Job1 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-JobBaseline_MethodsParamsJobs.Foo | Yes | - Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 1.37 | 0.00 | 2 | [Param=10]-JobBaseline_MethodsParamsJobs.Foo | No | + Foo | Job1 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-JobBaseline_MethodsParamsJobs.Foo | Yes | + Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 1.37 | 0.00 | 2 | [Param=10]-JobBaseline_MethodsParamsJobs.Foo | No | | | | | | | | | | | | - Bar | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-JobBaseline_MethodsParamsJobs.Bar | Yes | - Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 1.33 | 0.00 | 2 | [Param=10]-JobBaseline_MethodsParamsJobs.Bar | No | + Bar | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-JobBaseline_MethodsParamsJobs.Bar | Yes | + Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 1.33 | 0.00 | 2 | [Param=10]-JobBaseline_MethodsParamsJobs.Bar | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_Methods.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_Methods.verified.txt index 81f3e737f4..2355618257 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_Methods.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_Methods.verified.txt @@ -7,10 +7,10 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC DefaultJob : extra output line - Method | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |---------:|--------:|--------:|------:|--------:|-----:|------------- |--------- | - Base | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | DefaultJob | Yes | - Foo | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | DefaultJob | No | - Bar | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | DefaultJob | No | + Base | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | DefaultJob | Yes | + Foo | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | DefaultJob | No | + Bar | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | DefaultJob | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsJobs.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsJobs.verified.txt index df10b2d226..1096235ebb 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsJobs.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsJobs.verified.txt @@ -8,14 +8,14 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Job | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |----- |---------:|--------:|--------:|------:|--------:|-----:|------------- |--------- | - Base | Job1 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | Job1 | Yes | - Foo | Job1 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | Job1 | No | - Bar | Job1 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | Job1 | No | + Base | Job1 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | Job1 | Yes | + Foo | Job1 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | Job1 | No | + Bar | Job1 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | Job1 | No | | | | | | | | | | | - Base | Job2 | 402.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | Job2 | Yes | - Foo | Job2 | 502.0 ns | 6.09 ns | 1.58 ns | 1.25 | 0.00 | 2 | Job2 | No | - Bar | Job2 | 602.0 ns | 6.09 ns | 1.58 ns | 1.50 | 0.00 | 3 | Job2 | No | + Base | Job2 | 402.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | Job2 | Yes | + Foo | Job2 | 502.0 ns | 6.09 ns | 1.58 ns | 1.25 | 0.00 | 2 | Job2 | No | + Bar | Job2 | 602.0 ns | 6.09 ns | 1.58 ns | 1.50 | 0.00 | 3 | Job2 | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsParams.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsParams.verified.txt index bd2c4984bd..2178ec1d94 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsParams.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsParams.verified.txt @@ -7,14 +7,14 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC DefaultJob : extra output line - Method | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |------ |---------:|--------:|--------:|------:|--------:|-----:|---------------------- |--------- | - Base | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-DefaultJob | Yes | ^ - Foo | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | [Param=2]-DefaultJob | No | - Bar | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | [Param=2]-DefaultJob | No | + Base | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-DefaultJob | Yes | ^ + Foo | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | [Param=2]-DefaultJob | No | + Bar | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | [Param=2]-DefaultJob | No | | | | | | | | | | | - Base | 10 | 402.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-DefaultJob | Yes | ^ - Foo | 10 | 502.0 ns | 6.09 ns | 1.58 ns | 1.25 | 0.00 | 2 | [Param=10]-DefaultJob | No | - Bar | 10 | 602.0 ns | 6.09 ns | 1.58 ns | 1.50 | 0.00 | 3 | [Param=10]-DefaultJob | No | + Base | 10 | 402.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-DefaultJob | Yes | ^ + Foo | 10 | 502.0 ns | 6.09 ns | 1.58 ns | 1.25 | 0.00 | 2 | [Param=10]-DefaultJob | No | + Bar | 10 | 602.0 ns | 6.09 ns | 1.58 ns | 1.50 | 0.00 | 3 | [Param=10]-DefaultJob | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsParamsJobs.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsParamsJobs.verified.txt index f07268e77c..879a228733 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsParamsJobs.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodBaseline_MethodsParamsJobs.verified.txt @@ -8,22 +8,22 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Job | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |----- |------ |-----------:|--------:|--------:|------:|--------:|-----:|---------------- |--------- | - Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-Job1 | Yes | ^ - Foo | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | [Param=2]-Job1 | No | - Bar | Job1 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | [Param=2]-Job1 | No | + Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-Job1 | Yes | ^ + Foo | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | [Param=2]-Job1 | No | + Bar | Job1 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | [Param=2]-Job1 | No | | | | | | | | | | | | - Base | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-Job2 | Yes | - Foo | Job2 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 1.25 | 0.00 | 2 | [Param=2]-Job2 | No | - Bar | Job2 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 1.50 | 0.00 | 3 | [Param=2]-Job2 | No | + Base | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2]-Job2 | Yes | + Foo | Job2 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 1.25 | 0.00 | 2 | [Param=2]-Job2 | No | + Bar | Job2 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 1.50 | 0.00 | 3 | [Param=2]-Job2 | No | | | | | | | | | | | | - Base | Job1 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-Job1 | Yes | ^ - Foo | Job1 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | 1.14 | 0.00 | 2 | [Param=10]-Job1 | No | - Bar | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 1.28 | 0.00 | 3 | [Param=10]-Job1 | No | + Base | Job1 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-Job1 | Yes | ^ + Foo | Job1 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | 1.14 | 0.00 | 2 | [Param=10]-Job1 | No | + Bar | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 1.28 | 0.00 | 3 | [Param=10]-Job1 | No | | | | | | | | | | | | - Base | Job2 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-Job2 | Yes | - Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 1.10 | 0.00 | 2 | [Param=10]-Job2 | No | - Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 1.20 | 0.00 | 3 | [Param=10]-Job2 | No | + Base | Job2 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10]-Job2 | Yes | + Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 1.10 | 0.00 | 2 | [Param=10]-Job2 | No | + Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 1.20 | 0.00 | 3 | [Param=10]-Job2 | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodJobBaseline_MethodsJobs.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodJobBaseline_MethodsJobs.verified.txt index 00cfea261d..2e9f774250 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodJobBaseline_MethodsJobs.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodJobBaseline_MethodsJobs.verified.txt @@ -8,11 +8,11 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Job | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |----- |---------:|--------:|--------:|------:|--------:|-----:|------------- |--------- | - Foo | Job1 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | * | Yes | - Bar | Job1 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | * | No | - Foo | Job2 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | * | No | - Bar | Job2 | 402.0 ns | 6.09 ns | 1.58 ns | 3.94 | 0.05 | 4 | * | No | + Foo | Job1 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | * | Yes | + Bar | Job1 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | * | No | + Foo | Job2 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | * | No | + Bar | Job2 | 402.0 ns | 6.09 ns | 1.58 ns | 3.94 | 0.05 | 4 | * | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodJobBaseline_MethodsJobsParams.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodJobBaseline_MethodsJobsParams.verified.txt index 90a760d058..fac0a1a063 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodJobBaseline_MethodsJobsParams.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_MethodJobBaseline_MethodsJobsParams.verified.txt @@ -8,16 +8,16 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Job | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |----- |------ |---------:|--------:|--------:|------:|--------:|-----:|------------- |--------- | - Foo | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2] | Yes | ^ - Bar | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | [Param=2] | No | - Foo | Job2 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | [Param=2] | No | - Bar | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | 3.94 | 0.05 | 4 | [Param=2] | No | + Foo | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=2] | Yes | ^ + Bar | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | [Param=2] | No | + Foo | Job2 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 2.96 | 0.03 | 3 | [Param=2] | No | + Bar | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | 3.94 | 0.05 | 4 | [Param=2] | No | | | | | | | | | | | | - Foo | Job1 | 10 | 502.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10] | Yes | ^ - Bar | Job1 | 10 | 602.0 ns | 6.09 ns | 1.58 ns | 1.20 | 0.00 | 2 | [Param=10] | No | - Foo | Job2 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 1.40 | 0.00 | 3 | [Param=10] | No | - Bar | Job2 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | 1.60 | 0.00 | 4 | [Param=10] | No | + Foo | Job1 | 10 | 502.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | [Param=10] | Yes | ^ + Bar | Job1 | 10 | 602.0 ns | 6.09 ns | 1.58 ns | 1.20 | 0.00 | 2 | [Param=10] | No | + Foo | Job2 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 1.40 | 0.00 | 3 | [Param=10] | No | + Bar | Job2 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | 1.60 | 0.00 | 4 | [Param=10] | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs.verified.txt index 53b548d52c..52a50e2c03 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs.verified.txt @@ -8,19 +8,19 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Param | Mean | Error | StdDev | Rank | LogicalGroup | Baseline | + Method | Job | Param | Mean | Error | StdDev | Rank | LogicalGroup | Baseline | ------- |----- |------ |-----------:|--------:|--------:|-----:|------------- |--------- | - Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1 | * | No | ^ - Foo | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 2 | * | No | - Bar | Job1 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 3 | * | No | - Base | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | 4 | * | No | - Foo | Job2 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 5 | * | No | - Bar | Job2 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 6 | * | No | - Base | Job1 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 7 | * | No | ^ - Foo | Job1 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | 8 | * | No | - Bar | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 9 | * | No | - Base | Job2 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 10 | * | No | - Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 11 | * | No | - Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 12 | * | No | + Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1 | * | No | ^ + Foo | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 2 | * | No | + Bar | Job1 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 3 | * | No | + Base | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | 4 | * | No | + Foo | Job2 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 5 | * | No | + Bar | Job2 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 6 | * | No | + Base | Job1 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 7 | * | No | ^ + Foo | Job1 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | 8 | * | No | + Bar | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 9 | * | No | + Base | Job2 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 10 | * | No | + Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 11 | * | No | + Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 12 | * | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByAll.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByAll.verified.txt index 8063d96fe8..355aa0ebd1 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByAll.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByAll.verified.txt @@ -8,38 +8,38 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Job | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |----- |------ |-----------:|--------:|--------:|------:|--------:|-----:|---------------------------------------------------------------- |--------- | - A1 | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A1-Job1-[Param=2]-CatA | Yes | ^ + A1 | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A1-Job1-[Param=2]-CatA | Yes | ^ | | | | | | | | | | | - A1 | Job1 | 10 | 502.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A1-Job1-[Param=10]-CatA | Yes | ^ + A1 | Job1 | 10 | 502.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A1-Job1-[Param=10]-CatA | Yes | ^ | | | | | | | | | | | - A1 | Job2 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A1-Job2-[Param=2]-CatA | Yes | ^ + A1 | Job2 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A1-Job2-[Param=2]-CatA | Yes | ^ | | | | | | | | | | | - A1 | Job2 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A1-Job2-[Param=10]-CatA | Yes | ^ + A1 | Job2 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A1-Job2-[Param=10]-CatA | Yes | ^ | | | | | | | | | | | - A2 | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A2-Job1-[Param=2]-CatA | No | ^ + A2 | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A2-Job1-[Param=2]-CatA | No | ^ | | | | | | | | | | | - A2 | Job1 | 10 | 602.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A2-Job1-[Param=10]-CatA | No | ^ + A2 | Job1 | 10 | 602.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A2-Job1-[Param=10]-CatA | No | ^ | | | | | | | | | | | - A2 | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A2-Job2-[Param=2]-CatA | No | ^ + A2 | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A2-Job2-[Param=2]-CatA | No | ^ | | | | | | | | | | | - A2 | Job2 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A2-Job2-[Param=10]-CatA | No | ^ + A2 | Job2 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.A2-Job2-[Param=10]-CatA | No | ^ | | | | | | | | | | | - B1 | Job1 | 2 | 902.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B1-Job1-[Param=2]-CatB | Yes | ^ + B1 | Job1 | 2 | 902.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B1-Job1-[Param=2]-CatB | Yes | ^ | | | | | | | | | | | - B1 | Job1 | 10 | 1,302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B1-Job1-[Param=10]-CatB | Yes | ^ + B1 | Job1 | 10 | 1,302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B1-Job1-[Param=10]-CatB | Yes | ^ | | | | | | | | | | | - B1 | Job2 | 2 | 1,102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B1-Job2-[Param=2]-CatB | Yes | ^ + B1 | Job2 | 2 | 1,102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B1-Job2-[Param=2]-CatB | Yes | ^ | | | | | | | | | | | - B1 | Job2 | 10 | 1,502.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B1-Job2-[Param=10]-CatB | Yes | ^ + B1 | Job2 | 10 | 1,502.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B1-Job2-[Param=10]-CatB | Yes | ^ | | | | | | | | | | | - B2 | Job1 | 2 | 1,002.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B2-Job1-[Param=2]-CatB | No | ^ + B2 | Job1 | 2 | 1,002.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B2-Job1-[Param=2]-CatB | No | ^ | | | | | | | | | | | - B2 | Job1 | 10 | 1,402.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B2-Job1-[Param=10]-CatB | No | ^ + B2 | Job1 | 10 | 1,402.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B2-Job1-[Param=10]-CatB | No | ^ | | | | | | | | | | | - B2 | Job2 | 2 | 1,202.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B2-Job2-[Param=2]-CatB | No | ^ + B2 | Job2 | 2 | 1,202.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B2-Job2-[Param=2]-CatB | No | ^ | | | | | | | | | | | - B2 | Job2 | 10 | 1,602.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B2-Job2-[Param=10]-CatB | No | ^ + B2 | Job2 | 10 | 1,602.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | NoBaseline_MethodsParamsJobs_GroupByAll.B2-Job2-[Param=10]-CatB | No | ^ Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByCategory.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByCategory.verified.txt index 9ceee1be91..b586e21cd9 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByCategory.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByCategory.verified.txt @@ -8,30 +8,30 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | + Method | Job | Param | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline | ------- |----- |------ |-----------:|--------:|--------:|------:|--------:|-----:|--------------------- |--------- | - A1 | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatA-[Param=2]-Job1 | Yes | ^ - A2 | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | CatA-[Param=2]-Job1 | No | + A1 | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatA-[Param=2]-Job1 | Yes | ^ + A2 | Job1 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1.98 | 0.02 | 2 | CatA-[Param=2]-Job1 | No | | | | | | | | | | | | - A1 | Job2 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatA-[Param=2]-Job2 | Yes | - A2 | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | 1.33 | 0.00 | 2 | CatA-[Param=2]-Job2 | No | + A1 | Job2 | 2 | 302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatA-[Param=2]-Job2 | Yes | + A2 | Job2 | 2 | 402.0 ns | 6.09 ns | 1.58 ns | 1.33 | 0.00 | 2 | CatA-[Param=2]-Job2 | No | | | | | | | | | | | | - A1 | Job1 | 10 | 502.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatA-[Param=10]-Job1 | Yes | ^ - A2 | Job1 | 10 | 602.0 ns | 6.09 ns | 1.58 ns | 1.20 | 0.00 | 2 | CatA-[Param=10]-Job1 | No | + A1 | Job1 | 10 | 502.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatA-[Param=10]-Job1 | Yes | ^ + A2 | Job1 | 10 | 602.0 ns | 6.09 ns | 1.58 ns | 1.20 | 0.00 | 2 | CatA-[Param=10]-Job1 | No | | | | | | | | | | | | - A1 | Job2 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatA-[Param=10]-Job2 | Yes | - A2 | Job2 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | 1.14 | 0.00 | 2 | CatA-[Param=10]-Job2 | No | + A1 | Job2 | 10 | 702.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatA-[Param=10]-Job2 | Yes | + A2 | Job2 | 10 | 802.0 ns | 6.09 ns | 1.58 ns | 1.14 | 0.00 | 2 | CatA-[Param=10]-Job2 | No | | | | | | | | | | | | - B1 | Job1 | 2 | 902.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatB-[Param=2]-Job1 | Yes | ^ - B2 | Job1 | 2 | 1,002.0 ns | 6.09 ns | 1.58 ns | 1.11 | 0.00 | 2 | CatB-[Param=2]-Job1 | No | + B1 | Job1 | 2 | 902.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatB-[Param=2]-Job1 | Yes | ^ + B2 | Job1 | 2 | 1,002.0 ns | 6.09 ns | 1.58 ns | 1.11 | 0.00 | 2 | CatB-[Param=2]-Job1 | No | | | | | | | | | | | | - B1 | Job2 | 2 | 1,102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatB-[Param=2]-Job2 | Yes | - B2 | Job2 | 2 | 1,202.0 ns | 6.09 ns | 1.58 ns | 1.09 | 0.00 | 2 | CatB-[Param=2]-Job2 | No | + B1 | Job2 | 2 | 1,102.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatB-[Param=2]-Job2 | Yes | + B2 | Job2 | 2 | 1,202.0 ns | 6.09 ns | 1.58 ns | 1.09 | 0.00 | 2 | CatB-[Param=2]-Job2 | No | | | | | | | | | | | | - B1 | Job1 | 10 | 1,302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatB-[Param=10]-Job1 | Yes | ^ - B2 | Job1 | 10 | 1,402.0 ns | 6.09 ns | 1.58 ns | 1.08 | 0.00 | 2 | CatB-[Param=10]-Job1 | No | + B1 | Job1 | 10 | 1,302.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatB-[Param=10]-Job1 | Yes | ^ + B2 | Job1 | 10 | 1,402.0 ns | 6.09 ns | 1.58 ns | 1.08 | 0.00 | 2 | CatB-[Param=10]-Job1 | No | | | | | | | | | | | | - B1 | Job2 | 10 | 1,502.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatB-[Param=10]-Job2 | Yes | - B2 | Job2 | 10 | 1,602.0 ns | 6.09 ns | 1.58 ns | 1.07 | 0.00 | 2 | CatB-[Param=10]-Job2 | No | + B1 | Job2 | 10 | 1,502.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | CatB-[Param=10]-Job2 | Yes | + B2 | Job2 | 10 | 1,602.0 ns | 6.09 ns | 1.58 ns | 1.07 | 0.00 | 2 | CatB-[Param=10]-Job2 | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByJob.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByJob.verified.txt index a3b08eb9b9..9944b6dac1 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByJob.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByJob.verified.txt @@ -8,20 +8,20 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Param | Mean | Error | StdDev | Rank | LogicalGroup | Baseline | + Method | Job | Param | Mean | Error | StdDev | Rank | LogicalGroup | Baseline | ------- |----- |------ |-----------:|--------:|--------:|-----:|------------- |--------- | - Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1 | Job1 | No | ^ - Base | Job1 | 10 | 302.0 ns | 6.09 ns | 1.58 ns | 2 | Job1 | No | ^ - Foo | Job1 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 3 | Job1 | No | ^ - Bar | Job1 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 4 | Job1 | No | - Foo | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 5 | Job1 | No | ^ - Bar | Job1 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 6 | Job1 | No | + Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1 | Job1 | No | ^ + Base | Job1 | 10 | 302.0 ns | 6.09 ns | 1.58 ns | 2 | Job1 | No | ^ + Foo | Job1 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 3 | Job1 | No | ^ + Bar | Job1 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 4 | Job1 | No | + Foo | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 5 | Job1 | No | ^ + Bar | Job1 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 6 | Job1 | No | | | | | | | | | | - Base | Job2 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1 | Job2 | No | ^ - Base | Job2 | 10 | 402.0 ns | 6.09 ns | 1.58 ns | 2 | Job2 | No | ^ - Foo | Job2 | 2 | 702.0 ns | 6.09 ns | 1.58 ns | 3 | Job2 | No | ^ - Bar | Job2 | 2 | 802.0 ns | 6.09 ns | 1.58 ns | 4 | Job2 | No | - Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 5 | Job2 | No | ^ - Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 6 | Job2 | No | + Base | Job2 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 1 | Job2 | No | ^ + Base | Job2 | 10 | 402.0 ns | 6.09 ns | 1.58 ns | 2 | Job2 | No | ^ + Foo | Job2 | 2 | 702.0 ns | 6.09 ns | 1.58 ns | 3 | Job2 | No | ^ + Bar | Job2 | 2 | 802.0 ns | 6.09 ns | 1.58 ns | 4 | Job2 | No | + Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 5 | Job2 | No | ^ + Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 6 | Job2 | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByMethod.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByMethod.verified.txt index 9af19b0c2f..fd34bb1c63 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByMethod.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByMethod.verified.txt @@ -8,21 +8,21 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Param | Mean | Error | StdDev | Rank | LogicalGroup | Baseline | + Method | Job | Param | Mean | Error | StdDev | Rank | LogicalGroup | Baseline | ------- |----- |------ |-----------:|--------:|--------:|-----:|------------------------------------------------ |--------- | - Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1 | NoBaseline_MethodsParamsJobs_GroupByMethod.Base | No | ^ - Base | Job2 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 2 | NoBaseline_MethodsParamsJobs_GroupByMethod.Base | No | - Base | Job1 | 10 | 302.0 ns | 6.09 ns | 1.58 ns | 3 | NoBaseline_MethodsParamsJobs_GroupByMethod.Base | No | ^ - Base | Job2 | 10 | 402.0 ns | 6.09 ns | 1.58 ns | 4 | NoBaseline_MethodsParamsJobs_GroupByMethod.Base | No | + Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1 | NoBaseline_MethodsParamsJobs_GroupByMethod.Base | No | ^ + Base | Job2 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 2 | NoBaseline_MethodsParamsJobs_GroupByMethod.Base | No | + Base | Job1 | 10 | 302.0 ns | 6.09 ns | 1.58 ns | 3 | NoBaseline_MethodsParamsJobs_GroupByMethod.Base | No | ^ + Base | Job2 | 10 | 402.0 ns | 6.09 ns | 1.58 ns | 4 | NoBaseline_MethodsParamsJobs_GroupByMethod.Base | No | | | | | | | | | | - Foo | Job1 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 1 | NoBaseline_MethodsParamsJobs_GroupByMethod.Foo | No | ^ - Foo | Job2 | 2 | 702.0 ns | 6.09 ns | 1.58 ns | 2 | NoBaseline_MethodsParamsJobs_GroupByMethod.Foo | No | - Foo | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 3 | NoBaseline_MethodsParamsJobs_GroupByMethod.Foo | No | ^ - Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 4 | NoBaseline_MethodsParamsJobs_GroupByMethod.Foo | No | + Foo | Job1 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 1 | NoBaseline_MethodsParamsJobs_GroupByMethod.Foo | No | ^ + Foo | Job2 | 2 | 702.0 ns | 6.09 ns | 1.58 ns | 2 | NoBaseline_MethodsParamsJobs_GroupByMethod.Foo | No | + Foo | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 3 | NoBaseline_MethodsParamsJobs_GroupByMethod.Foo | No | ^ + Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 4 | NoBaseline_MethodsParamsJobs_GroupByMethod.Foo | No | | | | | | | | | | - Bar | Job1 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 1 | NoBaseline_MethodsParamsJobs_GroupByMethod.Bar | No | ^ - Bar | Job2 | 2 | 802.0 ns | 6.09 ns | 1.58 ns | 2 | NoBaseline_MethodsParamsJobs_GroupByMethod.Bar | No | - Bar | Job1 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 3 | NoBaseline_MethodsParamsJobs_GroupByMethod.Bar | No | ^ - Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 4 | NoBaseline_MethodsParamsJobs_GroupByMethod.Bar | No | + Bar | Job1 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 1 | NoBaseline_MethodsParamsJobs_GroupByMethod.Bar | No | ^ + Bar | Job2 | 2 | 802.0 ns | 6.09 ns | 1.58 ns | 2 | NoBaseline_MethodsParamsJobs_GroupByMethod.Bar | No | + Bar | Job1 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 3 | NoBaseline_MethodsParamsJobs_GroupByMethod.Bar | No | ^ + Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 4 | NoBaseline_MethodsParamsJobs_GroupByMethod.Bar | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByParams.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByParams.verified.txt index 2556795c77..83dac489bd 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByParams.verified.txt +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_NoBaseline_MethodsParamsJobs_GroupByParams.verified.txt @@ -8,20 +8,20 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC Job2 : extra output line - Method | Job | Param | Mean | Error | StdDev | Rank | LogicalGroup | Baseline | + Method | Job | Param | Mean | Error | StdDev | Rank | LogicalGroup | Baseline | ------- |----- |------ |-----------:|--------:|--------:|-----:|------------- |--------- | - Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1 | [Param=2] | No | ^ - Base | Job2 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 2 | [Param=2] | No | - Foo | Job1 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 3 | [Param=2] | No | - Bar | Job1 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 4 | [Param=2] | No | - Foo | Job2 | 2 | 702.0 ns | 6.09 ns | 1.58 ns | 5 | [Param=2] | No | - Bar | Job2 | 2 | 802.0 ns | 6.09 ns | 1.58 ns | 6 | [Param=2] | No | + Base | Job1 | 2 | 102.0 ns | 6.09 ns | 1.58 ns | 1 | [Param=2] | No | ^ + Base | Job2 | 2 | 202.0 ns | 6.09 ns | 1.58 ns | 2 | [Param=2] | No | + Foo | Job1 | 2 | 502.0 ns | 6.09 ns | 1.58 ns | 3 | [Param=2] | No | + Bar | Job1 | 2 | 602.0 ns | 6.09 ns | 1.58 ns | 4 | [Param=2] | No | + Foo | Job2 | 2 | 702.0 ns | 6.09 ns | 1.58 ns | 5 | [Param=2] | No | + Bar | Job2 | 2 | 802.0 ns | 6.09 ns | 1.58 ns | 6 | [Param=2] | No | | | | | | | | | | - Base | Job1 | 10 | 302.0 ns | 6.09 ns | 1.58 ns | 1 | [Param=10] | No | ^ - Base | Job2 | 10 | 402.0 ns | 6.09 ns | 1.58 ns | 2 | [Param=10] | No | - Foo | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 3 | [Param=10] | No | - Bar | Job1 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 4 | [Param=10] | No | - Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 5 | [Param=10] | No | - Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 6 | [Param=10] | No | + Base | Job1 | 10 | 302.0 ns | 6.09 ns | 1.58 ns | 1 | [Param=10] | No | ^ + Base | Job2 | 10 | 402.0 ns | 6.09 ns | 1.58 ns | 2 | [Param=10] | No | + Foo | Job1 | 10 | 902.0 ns | 6.09 ns | 1.58 ns | 3 | [Param=10] | No | + Bar | Job1 | 10 | 1,002.0 ns | 6.09 ns | 1.58 ns | 4 | [Param=10] | No | + Foo | Job2 | 10 | 1,102.0 ns | 6.09 ns | 1.58 ns | 5 | [Param=10] | No | + Bar | Job2 | 10 | 1,202.0 ns | 6.09 ns | 1.58 ns | 6 | [Param=10] | No | Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs b/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs index b51f978edf..ce54b83bbb 100644 --- a/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs +++ b/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs @@ -6,12 +6,15 @@ using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Reports; using BenchmarkDotNet.Running; using BenchmarkDotNet.Tests.Builders; using BenchmarkDotNet.Toolchains; using BenchmarkDotNet.Toolchains.Results; using BenchmarkDotNet.Validators; +using Perfolizer.Horology; +using static BenchmarkDotNet.Reports.SummaryTable.SummaryTableColumn; namespace BenchmarkDotNet.Tests.Mocks { @@ -33,40 +36,49 @@ public static Summary CreateSummary(Type benchmarkType) } public static Summary CreateSummary(IConfig config) => new Summary( - "MockSummary", - CreateReports(config), - new HostEnvironmentInfoBuilder().WithoutDotNetSdkVersion().Build(), - string.Empty, - string.Empty, - TimeSpan.FromMinutes(1), - config.CultureInfo, - ImmutableArray.Empty, - ImmutableArray.Empty); + "MockSummary", + CreateReports(config), + new HostEnvironmentInfoBuilder().WithoutDotNetSdkVersion().Build(), + string.Empty, + string.Empty, + TimeSpan.FromMinutes(1), + config.CultureInfo, + ImmutableArray.Empty, + ImmutableArray.Empty, + config.SummaryStyle); + public static Summary CreateSummary(IConfig config, bool hugeSd, Metric[] metrics) => CreateSummary(config, hugeSd, metrics); public static Summary CreateSummary(IConfig config, bool hugeSd, Metric[] metrics) => new Summary( - "MockSummary", - CreateBenchmarks(config).Select(b => CreateReport(b, hugeSd, metrics)).ToImmutableArray(), - new HostEnvironmentInfoBuilder().Build(), - string.Empty, - string.Empty, - TimeSpan.FromMinutes(1), - TestCultureInfo.Instance, - ImmutableArray.Empty, - ImmutableArray.Empty); + "MockSummary", + CreateBenchmarks(config).Select(b => CreateReport(b, hugeSd, metrics)).ToImmutableArray(), + new HostEnvironmentInfoBuilder().Build(), + string.Empty, + string.Empty, + TimeSpan.FromMinutes(1), + TestCultureInfo.Instance, + ImmutableArray.Empty, + ImmutableArray.Empty); public static Summary CreateSummary(IConfig config, bool hugeSd, Func metricsBuilder) => new Summary( - "MockSummary", - CreateBenchmarks(config).Select(b => CreateReport(b, hugeSd, metricsBuilder(b))).ToImmutableArray(), - new HostEnvironmentInfoBuilder().Build(), - string.Empty, - string.Empty, - TimeSpan.FromMinutes(1), - TestCultureInfo.Instance, - ImmutableArray.Empty, - ImmutableArray.Empty); + "MockSummary", + CreateBenchmarks(config).Select(b => CreateReport(b, hugeSd, metricsBuilder(b))).ToImmutableArray(), + new HostEnvironmentInfoBuilder().Build(), + string.Empty, + string.Empty, + TimeSpan.FromMinutes(1), + TestCultureInfo.Instance, + ImmutableArray.Empty, + ImmutableArray.Empty); + + public static SummaryStyle CreateSummaryStyle(bool printUnitsInHeader = false, bool printUnitsInContent = true, bool printZeroValuesInContent = false, + SizeUnit sizeUnit = null, TimeUnit timeUnit = null, TextJustification textColumnJustification = TextJustification.Left, + TextJustification numericColumnJustification = TextJustification.Left) + => new SummaryStyle(DefaultCultureInfo.Instance, printUnitsInHeader, sizeUnit, timeUnit, printUnitsInContent: printUnitsInContent, + printZeroValuesInContent: printZeroValuesInContent, textColumnJustification: textColumnJustification, + numericColumnJustification: numericColumnJustification); private static ImmutableArray CreateReports(IConfig config) => CreateBenchmarks(config).Select(CreateSimpleReport).ToImmutableArray(); diff --git a/tests/BenchmarkDotNet.Tests/Reports/SummaryTableTests.cs b/tests/BenchmarkDotNet.Tests/Reports/SummaryTableTests.cs index f33b8604e2..ebea88b5ad 100644 --- a/tests/BenchmarkDotNet.Tests/Reports/SummaryTableTests.cs +++ b/tests/BenchmarkDotNet.Tests/Reports/SummaryTableTests.cs @@ -65,6 +65,28 @@ public void TextColumnIsLeftJustified() Assert.Equal(SummaryTable.SummaryTableColumn.TextJustification.Left, table.Columns.First(c => c.Header == "Param").Justify); } + [Fact] + public void NumericColumnWithLeftJustification() + { + var config = ManualConfig.Create(DefaultConfig.Instance).AddColumn(StatisticColumn.Mean); + config.SummaryStyle = MockFactory.CreateSummaryStyle(numericColumnJustification: SummaryTable.SummaryTableColumn.TextJustification.Left); + var summary = MockFactory.CreateSummary(config); + var table = new SummaryTable(summary); + + Assert.Equal(SummaryTable.SummaryTableColumn.TextJustification.Left, table.Columns.First(c => c.Header == "Mean").Justify); + } + + [Fact] + public void TextColumnWithRightJustification() + { + var config = ManualConfig.Create(DefaultConfig.Instance).AddColumn(new ParamColumn("Param")); + config.SummaryStyle = MockFactory.CreateSummaryStyle(textColumnJustification: SummaryTable.SummaryTableColumn.TextJustification.Right); + var summary = MockFactory.CreateSummary(config); + var table = new SummaryTable(summary); + + Assert.Equal(SummaryTable.SummaryTableColumn.TextJustification.Right, table.Columns.First(c => c.Header == "Param").Justify); + } + [Fact] // Issue #1070 public void CustomOrdererIsSupported() { From e2e888c694854974c9e11e3fd92c278876637903 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 18 Aug 2023 02:27:58 -0400 Subject: [PATCH 14/15] Use Roslyn Toolchain by default if no build settings are changed. --- .../Toolchains/ToolchainExtensions.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs index 66f17ce246..ad3b4a5a6b 100644 --- a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs +++ b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs @@ -28,19 +28,17 @@ private static IToolchain GetToolchain(Job job, Descriptor descriptor) : GetToolchain( job.ResolveValue(EnvironmentMode.RuntimeCharacteristic, EnvironmentResolver.Instance), descriptor, - job.HasValue(InfrastructureMode.NuGetReferencesCharacteristic) || job.HasValue(InfrastructureMode.BuildConfigurationCharacteristic)); + job.HasValue(InfrastructureMode.NuGetReferencesCharacteristic) + || job.HasValue(InfrastructureMode.BuildConfigurationCharacteristic) + || job.HasValue(InfrastructureMode.ArgumentsCharacteristic)); internal static IToolchain GetToolchain(this Runtime runtime, Descriptor descriptor = null, bool preferMsBuildToolchains = false) { switch (runtime) { case ClrRuntime clrRuntime: - if (!preferMsBuildToolchains && RuntimeInformation.IsFullFramework && - // If dotnet SDK is not installed, we use RoslynToolchain. - (!HostEnvironmentInfo.GetCurrent().IsDotNetCliInstalled() - // Integration tests take too much time, because each benchmark run rebuilds the test suite and BenchmarkDotNet itself. - // To reduce the total duration of the CI workflows, we just use RoslynToolchain. - || XUnitHelper.IsIntegrationTest.Value)) + if (!preferMsBuildToolchains && RuntimeInformation.IsFullFramework + && RuntimeInformation.GetCurrentRuntime().MsBuildMoniker == runtime.MsBuildMoniker) { return RoslynToolchain.Instance; } From f8de1e95a7a2c5d06862c14f5f657142d54e9328 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Fri, 8 Sep 2023 11:30:30 +0200 Subject: [PATCH 15/15] Prepare v0.13.8 changelog --- README.md | 2 +- docs/_changelog/footer/v0.13.8.md | 11 +++++++++++ docs/_changelog/header/v0.13.8.md | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 docs/_changelog/footer/v0.13.8.md create mode 100644 docs/_changelog/header/v0.13.8.md diff --git a/README.md b/README.md index df84b5d462..424439148a 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ It's no harder than writing unit tests! Under the hood, it performs a lot of [magic](#automation) that guarantees [reliable and precise](#reliability) results thanks to the [perfolizer](https://github.com/AndreyAkinshin/perfolizer) statistical engine. BenchmarkDotNet protects you from popular benchmarking mistakes and warns you if something is wrong with your benchmark design or obtained measurements. The results are presented in a [user-friendly](#friendliness) form that highlights all the important facts about your experiment. -BenchmarkDotNet is already adopted by [16900+ GitHub projects](https://github.com/dotnet/BenchmarkDotNet/network/dependents) including +BenchmarkDotNet is already adopted by [17400+ GitHub projects](https://github.com/dotnet/BenchmarkDotNet/network/dependents) including [.NET Runtime](https://github.com/dotnet/runtime), [.NET Compiler](https://github.com/dotnet/roslyn), [.NET Performance](https://github.com/dotnet/performance), diff --git a/docs/_changelog/footer/v0.13.8.md b/docs/_changelog/footer/v0.13.8.md new file mode 100644 index 0000000000..a8551cc47f --- /dev/null +++ b/docs/_changelog/footer/v0.13.8.md @@ -0,0 +1,11 @@ +_Date: September 08, 2023_ + +_Milestone: [v0.13.8](https://github.com/dotnet/BenchmarkDotNet/issues?q=milestone%3Av0.13.8)_ +([List of commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.7...v0.13.8)) + +_NuGet Packages:_ +* https://www.nuget.org/packages/BenchmarkDotNet/0.13.8 +* https://www.nuget.org/packages/BenchmarkDotNet.Annotations/0.13.8 +* https://www.nuget.org/packages/BenchmarkDotNet.Diagnostics.dotTrace/0.13.8 +* https://www.nuget.org/packages/BenchmarkDotNet.Diagnostics.Windows/0.13.8 +* https://www.nuget.org/packages/BenchmarkDotNet.Templates/0.13.8 diff --git a/docs/_changelog/header/v0.13.8.md b/docs/_changelog/header/v0.13.8.md new file mode 100644 index 0000000000..b9222853e2 --- /dev/null +++ b/docs/_changelog/header/v0.13.8.md @@ -0,0 +1,3 @@ +## Highlights + +This release contains important bug fixes. \ No newline at end of file