From e02e55e5fdf70511cfb53cab64f368b972272406 Mon Sep 17 00:00:00 2001
From: Gabriel Bider <1554615+silkfire@users.noreply.github.com>
Date: Fri, 26 Sep 2025 23:37:56 +0200
Subject: [PATCH 01/24] Add Roslyn analyzers to detect incorrect usage of
BenchmarkDotNet
---
.gitignore | 3 +
BenchmarkDotNet.Analyzers.sln | 55 +
NuGet.Config | 39 +-
.../BenchmarkDotNet.Analyzers.Package.csproj | 36 +
.../tools/install.ps1 | 250 ++++
.../tools/uninstall.ps1 | 257 ++++
.../ArgumentsAttributeAnalyzerTests.cs | 750 ++++++++++
...GeneralParameterAttributesAnalyzerTests.cs | 1332 +++++++++++++++++
.../ParamsAllValuesAttributeAnalyzerTests.cs | 237 +++
.../ParamsAttributeAnalyzerTests.cs | 678 +++++++++
.../BenchmarkRunner/RunAnalyzerTests.cs | 124 ++
.../General/BenchmarkClassAnalyzerTests.cs | 777 ++++++++++
.../BenchmarkDotNet.Analyzers.Tests.csproj | 53 +
...kDotNet.Analyzers.Tests.csproj.DotSettings | 5 +
.../Fixtures/AnalyzerTestFixture.cs | 234 +++
.../Extensions/TheoryDataExtensions.cs | 14 +
.../Generators/CombinationsGenerator.cs | 66 +
.../FieldOrPropertyDeclarationTheoryData.cs | 19 +
...NonPublicClassAccessModifiersTheoryData.cs | 20 +
...licClassMemberAccessModifiersTheoryData.cs | 19 +
...PropertySetterAccessModifiersTheoryData.cs | 18 +
.../BenchmarkDotNet.Analyzers.Vsix.csproj | 47 +
.../source.extension.vsixmanifest | 24 +
.../AnalyzerHelper.cs | 73 +
.../AnalyzerReleases.Shipped.md | 2 +
.../AnalyzerReleases.Unshipped.md | 35 +
.../Attributes/ArgumentsAttributeAnalyzer.cs | 331 ++++
.../GeneralParameterAttributesAnalyzer.cs | 329 ++++
.../ParamsAllValuesAttributeAnalyzer.cs | 133 ++
.../Attributes/ParamsAttributeAnalyzer.cs | 283 ++++
.../BenchmarkDotNet.Analyzers.csproj | 38 +
...nchmarkDotNetAnalyzerResources.Designer.cs | 778 ++++++++++
.../BenchmarkDotNetAnalyzerResources.resx | 355 +++++
.../BenchmarkRunner/RunAnalyzer.cs | 128 ++
.../DiagnosticIds.cs | 34 +
.../General/BenchmarkClassAnalyzer.cs | 286 ++++
.../BenchmarkDotNet.Annotations.csproj | 6 +
.../BenchmarkDotNet.Disassembler.x64.csproj | 45 +-
.../BenchmarkDotNet.Disassembler.x86.csproj | 57 +-
39 files changed, 7904 insertions(+), 66 deletions(-)
create mode 100644 BenchmarkDotNet.Analyzers.sln
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/BenchmarkDotNet.Analyzers.Package.csproj
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/tools/install.ps1
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/tools/uninstall.ps1
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/GeneralParameterAttributesAnalyzerTests.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ParamsAllValuesAttributeAnalyzerTests.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ParamsAttributeAnalyzerTests.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/BenchmarkRunner/RunAnalyzerTests.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/General/BenchmarkClassAnalyzerTests.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/BenchmarkDotNet.Analyzers.Tests.csproj
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/BenchmarkDotNet.Analyzers.Tests.csproj.DotSettings
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/Fixtures/AnalyzerTestFixture.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/Fixtures/Extensions/TheoryDataExtensions.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/Fixtures/Generators/CombinationsGenerator.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/Fixtures/TheoryData/FieldOrPropertyDeclarationTheoryData.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/Fixtures/TheoryData/NonPublicClassAccessModifiersTheoryData.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/Fixtures/TheoryData/NonPublicClassMemberAccessModifiersTheoryData.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/Fixtures/TheoryData/NonPublicPropertySetterAccessModifiersTheoryData.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Vsix/BenchmarkDotNet.Analyzers.Vsix.csproj
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Vsix/source.extension.vsixmanifest
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/AnalyzerReleases.Shipped.md
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/AnalyzerReleases.Unshipped.md
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/Attributes/GeneralParameterAttributesAnalyzer.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/Attributes/ParamsAllValuesAttributeAnalyzer.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/Attributes/ParamsAttributeAnalyzer.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.csproj
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/BenchmarkDotNetAnalyzerResources.Designer.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/BenchmarkDotNetAnalyzerResources.resx
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/BenchmarkRunner/RunAnalyzer.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/DiagnosticIds.cs
create mode 100644 src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers/General/BenchmarkClassAnalyzer.cs
diff --git a/.gitignore b/.gitignore
index 96012efb78..ac368b610f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,6 +54,9 @@ src/BenchmarkDotNet/Disassemblers/BenchmarkDotNet.Disassembler.*.nupkg
# Visual Studio 2015 cache/options directory
.vs/
+# VSCode directory
+.vscode/
+
# Cake
tools/**
.dotnet
diff --git a/BenchmarkDotNet.Analyzers.sln b/BenchmarkDotNet.Analyzers.sln
new file mode 100644
index 0000000000..b82b4873d2
--- /dev/null
+++ b/BenchmarkDotNet.Analyzers.sln
@@ -0,0 +1,55 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31710.8
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BenchmarkDotNet.Analyzers", "src\BenchmarkDotNet.Analyzers\BenchmarkDotNet.Analyzers\BenchmarkDotNet.Analyzers.csproj", "{B7664DD5-DCDB-4324-91A9-16D242CC4498}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BenchmarkDotNet.Analyzers.Package", "src\BenchmarkDotNet.Analyzers\BenchmarkDotNet.Analyzers.Package\BenchmarkDotNet.Analyzers.Package.csproj", "{B7500BDE-4DC7-4858-968F-11889AA4F289}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BenchmarkDotNet.Analyzers.Tests", "src\BenchmarkDotNet.Analyzers\BenchmarkDotNet.Analyzers.Tests\BenchmarkDotNet.Analyzers.Tests.csproj", "{5D1F1A9E-681D-456B-A838-2EAAAD24BC7D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BenchmarkDotNet.Analyzers.Vsix", "src\BenchmarkDotNet.Analyzers\BenchmarkDotNet.Analyzers.Vsix\BenchmarkDotNet.Analyzers.Vsix.csproj", "{F3163F56-3EC2-44F8-872E-02E3114D7849}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BenchmarkDotNet", "src\BenchmarkDotNet\BenchmarkDotNet.csproj", "{B5F58AA0-88F8-4C8C-B734-E1217E23079E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BenchmarkDotNet.Annotations", "src\BenchmarkDotNet.Annotations\BenchmarkDotNet.Annotations.csproj", "{F07A7F74-15B6-4DC6-8617-A3A9C11C71EF}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B7664DD5-DCDB-4324-91A9-16D242CC4498}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B7664DD5-DCDB-4324-91A9-16D242CC4498}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B7664DD5-DCDB-4324-91A9-16D242CC4498}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B7664DD5-DCDB-4324-91A9-16D242CC4498}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B7500BDE-4DC7-4858-968F-11889AA4F289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B7500BDE-4DC7-4858-968F-11889AA4F289}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B7500BDE-4DC7-4858-968F-11889AA4F289}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B7500BDE-4DC7-4858-968F-11889AA4F289}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5D1F1A9E-681D-456B-A838-2EAAAD24BC7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5D1F1A9E-681D-456B-A838-2EAAAD24BC7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5D1F1A9E-681D-456B-A838-2EAAAD24BC7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5D1F1A9E-681D-456B-A838-2EAAAD24BC7D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F3163F56-3EC2-44F8-872E-02E3114D7849}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F3163F56-3EC2-44F8-872E-02E3114D7849}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F3163F56-3EC2-44F8-872E-02E3114D7849}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F3163F56-3EC2-44F8-872E-02E3114D7849}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B5F58AA0-88F8-4C8C-B734-E1217E23079E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B5F58AA0-88F8-4C8C-B734-E1217E23079E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B5F58AA0-88F8-4C8C-B734-E1217E23079E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B5F58AA0-88F8-4C8C-B734-E1217E23079E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F07A7F74-15B6-4DC6-8617-A3A9C11C71EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F07A7F74-15B6-4DC6-8617-A3A9C11C71EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F07A7F74-15B6-4DC6-8617-A3A9C11C71EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F07A7F74-15B6-4DC6-8617-A3A9C11C71EF}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {27411BE6-6445-400B-AB04-29B993B39CFF}
+ EndGlobalSection
+EndGlobal
diff --git a/NuGet.Config b/NuGet.Config
index 7507704b8b..0ed38438e6 100644
--- a/NuGet.Config
+++ b/NuGet.Config
@@ -1,18 +1,21 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/BenchmarkDotNet.Analyzers.Package.csproj b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/BenchmarkDotNet.Analyzers.Package.csproj
new file mode 100644
index 0000000000..cfd362d45a
--- /dev/null
+++ b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/BenchmarkDotNet.Analyzers.Package.csproj
@@ -0,0 +1,36 @@
+
+
+
+ netstandard2.0
+ false
+ true
+ true
+
+
+
+ BenchmarkDotNet.Analyzers
+ 1.0.0.0
+
+ Analyzers for the BenchmarkDotNet package.
+
+ true
+ true
+
+ $(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/tools/install.ps1 b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/tools/install.ps1
new file mode 100644
index 0000000000..be2f74c118
--- /dev/null
+++ b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/tools/install.ps1
@@ -0,0 +1,250 @@
+param($installPath, $toolsPath, $package, $project)
+
+if($project.Object.SupportsPackageDependencyResolution)
+{
+ if($project.Object.SupportsPackageDependencyResolution())
+ {
+ # Do not install analyzers via install.ps1, instead let the project system handle it.
+ return
+ }
+}
+
+$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ if (Test-Path $analyzersPath)
+ {
+ # Install the language agnostic analyzers.
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
+
+# $project.Type gives the language name like (C# or VB.NET)
+$languageFolder = ""
+if($project.Type -eq "C#")
+{
+ $languageFolder = "cs"
+}
+if($project.Type -eq "VB.NET")
+{
+ $languageFolder = "vb"
+}
+if($languageFolder -eq "")
+{
+ return
+}
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Install language specific analyzers.
+ $languageAnalyzersPath = join-path $analyzersPath $languageFolder
+ if (Test-Path $languageAnalyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
+# SIG # Begin signature block
+# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA/i+qRUHsWzI0s
+# FVk99zLgt/HOEQ33uvkFsWtHTHZgf6CCDYEwggX/MIID56ADAgECAhMzAAAB32vw
+# LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
+# bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw
+# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
+# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+# AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn
+# s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw
+# PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS
+# yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG
+# 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh
+# EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
+# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw
+# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
+# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu
+# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
+# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
+# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
+# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
+# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH
+# tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS
+# 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp
+# TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok
+# t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4
+# b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao
+# mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD
+# Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt
+# VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G
+# CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+
+# Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82
+# oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS
+# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
+# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
+# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
+# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
+# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
+# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
+# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
+# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
+# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
+# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
+# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
+# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
+# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
+# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
+# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
+# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
+# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
+# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
+# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
+# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
+# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
+# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
+# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
+# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
+# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
+# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
+# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
+# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
+# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
+# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
+# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
+# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
+# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
+# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
+# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
+# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
+# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
+# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
+# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
+# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG
+# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
+# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
+# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN
+# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
+# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgRjg7DcI6
+# uhYfXWwAQ6hK0mPW7iyr2tzHR0DHSDJkscIwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
+# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
+# BgkqhkiG9w0BAQEFAASCAQCt+qfRFudb8jpGDEFmJnD3uqC44a6SmTO+nFx0s2tf
+# o+OTFNnRhwUMuTz/KVCfSwTzD/p2mD6JmuOuNmtTKyanjVNbYvwzO7LWedJh3d+T
+# WEKZ6KYFbm6rPM+9DIINZNuK5Vb15vvNBUYI4PgFrSLGXdmRIB5xGiLRYWM/UET/
+# Sb4T6edTQKYx4vkDX9UcM4cYCx1u59hR6FgdCCHzU9/ZHYqN0AhBrHrTWGuqxx3E
+# Oo0wdYJMRLH8zPFbzRNcG4qVlq95yDtWqzNcYMWybejIZenDg6am3ZldQFMoGU38
+# 76WP/a5unw8DKpkL/4ZO686G9Boh5Jc6U8mMGlLctW43oYIS8TCCEu0GCisGAQQB
+# gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME
+# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB
+# MDEwDQYJYIZIAWUDBAIBBQAEIITxzR9P1o4UBFnvUGa4yCqvmQhov1ZeA/XM1qBB
+# 5/5xAgZgieV7UmkYEzIwMjEwNTEzMTkwNDA3LjM3MVowBIACAfSggdSkgdEwgc4x
+# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
+# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p
+# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg
+# VFNTIEVTTjpEOURFLUUzOUEtNDNGRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
+# U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABYfWiM16gKiRpAAAA
+# AAFhMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
+# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
+# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
+# MB4XDTIxMDExNDE5MDIyMVoXDTIyMDQxMTE5MDIyMVowgc4xCzAJBgNVBAYTAlVT
+# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
+# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy
+# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpEOURF
+# LUUzOUEtNDNGRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj
+# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJeInahBrU//GzTqhxUy
+# AC8UXct6UJCkb2xEZKV3gjggmLAheBrxJk7tH+Pw2tTcyarLRfmV2xo5oBk5pW/O
+# cDc/n/TcTeQU6JIN5PlTcn0C9RlKQ6t9OuU/WAyAxGTjKE4ENnUjXtxiNlD/K2ZG
+# MLvjpROBKh7TtkUJK6ZGWw/uTRabNBxRg13TvjkGHXEUEDJ8imacw9BCeR9L6und
+# r32tj4duOFIHD8m1es3SNN98Zq4IDBP3Ccb+HQgxpbeHIUlK0y6zmzIkvfN73Zxw
+# fGvFv0/Max79WJY0cD8poCnZFijciWrf0eD1T2/+7HgewzrdxPdSFockUQ8QovID
+# IYkCAwEAAaOCARswggEXMB0GA1UdDgQWBBRWHpqd1hv71SVj5LAdPfNE7PhLLzAf
+# BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH
+# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU
+# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF
+# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0
+# YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG
+# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQAQTA9bqVBmx5TTMhzj+Q8zWkPQXgCc
+# SQiqy2YYWF0hWr5GEiN2LtA+EWdu1y8oysZau4CP7SzM8VTSq31CLJiOy39Z4RvE
+# q2mr0EftFvmX2CxQ7ZyzrkhWMZaZQLkYbH5oabIFwndW34nh980BOY395tfnNS/Y
+# 6N0f+jXdoFn7fI2c43TFYsUqIPWjOHJloMektlD6/uS6Zn4xse/lItFm+fWOcB2A
+# xyXEB3ZREeSg9j7+GoEl1xT/iJuV/So7TlWdwyacQu4lv3MBsvxzRIbKhZwrDYog
+# moyJ+rwgQB8mKS4/M1SDRtIptamoTFJ56Tk6DuUXx1JudToelgjEZPa5MIIGcTCC
+# BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC
+# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
+# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv
+# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN
+# MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
+# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
+# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw
+# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0
+# VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw
+# RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe
+# dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx
+# Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G
+# kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA
+# AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7
+# fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
+# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
+# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
+# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
+# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
+# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g
+# AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93
+# d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB
+# BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA
+# bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh
+# IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS
+# +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK
+# kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon
+# /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi
+# PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/
+# fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII
+# YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0
+# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a
+# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ
+# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+
+# NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
+# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
+# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP
+# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpE
+# OURFLUUzOUEtNDNGRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
+# dmljZaIjCgEBMAcGBSsOAwIaAxUAFW5ShAw5ekTEXvL/4V1s0rbDz3mggYMwgYCk
+# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
+# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
+# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
+# AORHgWMwIhgPMjAyMTA1MTMxNDQzNDdaGA8yMDIxMDUxNDE0NDM0N1owdzA9Bgor
+# BgEEAYRZCgQBMS8wLTAKAgUA5EeBYwIBADAKAgEAAgIYbAIB/zAHAgEAAgIRJjAK
+# AgUA5EjS4wIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
+# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAJ7JAjVdIqigM77q
+# X+WQlagNMRjqaFC99qAjVz3kuffmZJoaEZAUFl+ynf6+HFfOhtbygpb3Inb1ewPz
+# sZH0SoRd1eGUpvXk0rzjFl8jKiV/FWTV/xJDdRyKf4I6Pl4hzA1gpsB0sNO3Qqr3
+# u8dTOzbh3DWucOQgfLBWoq3e/UuUMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC
+# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
+# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
+# bWUtU3RhbXAgUENBIDIwMTACEzMAAAFh9aIzXqAqJGkAAAAAAWEwDQYJYIZIAWUD
+# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
+# CQQxIgQgTf39WlLL62P8pSZibw+7Xw+a3N3OO0YRek+60a+WpNswgfoGCyqGSIb3
+# DQEJEAIvMYHqMIHnMIHkMIG9BCBhz4un6mkSLd/zA+0N5YLDGp4vW/VBtNW/lpmh
+# tAk4bzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
+# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
+# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
+# YfWiM16gKiRpAAAAAAFhMCIEIIqi7Un+0eNrtRg58qtA+fKclBz8FTjtf7MCNv7U
+# MEInMA0GCSqGSIb3DQEBCwUABIIBADPu+hGKCfhBayhOOzglyKPK9RBeNCsRzMKp
+# Uymhnad7xNyu+78hq0nWjS/DWgiCYRSF4g0Hl2ls9AfHbz7vT0GLZelJOfaTW+M7
+# gA6HGctwkDhfQg1tG0+pJ5D+pdCrvlMzp4K0EF5pE8FSib3BWOIBu5Ja4D4IbmE4
+# 3kkbHw/FWQLJDEhPFLIpQg45p4dsMLlR39QaPXQpX3hu2Tp+LgzQYA+meIpt95W0
+# gKA/jb0H26x7TncDwyi5bgMt7cKDhkiLSm6y1yHDnd9yJa3XkbcU9Ez7MjEBvG35
+# RXImHA84+QsRDHzx8MlAjy8f3ln/JwUt/U/OtGl43qbTKt/ZX78=
+# SIG # End signature block
diff --git a/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/tools/uninstall.ps1 b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/tools/uninstall.ps1
new file mode 100644
index 0000000000..af3d04f297
--- /dev/null
+++ b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Package/tools/uninstall.ps1
@@ -0,0 +1,257 @@
+param($installPath, $toolsPath, $package, $project)
+
+if($project.Object.SupportsPackageDependencyResolution)
+{
+ if($project.Object.SupportsPackageDependencyResolution())
+ {
+ # Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it.
+ return
+ }
+}
+
+$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Uninstall the language agnostic analyzers.
+ if (Test-Path $analyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
+
+# $project.Type gives the language name like (C# or VB.NET)
+$languageFolder = ""
+if($project.Type -eq "C#")
+{
+ $languageFolder = "cs"
+}
+if($project.Type -eq "VB.NET")
+{
+ $languageFolder = "vb"
+}
+if($languageFolder -eq "")
+{
+ return
+}
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Uninstall language specific analyzers.
+ $languageAnalyzersPath = join-path $analyzersPath $languageFolder
+ if (Test-Path $languageAnalyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ try
+ {
+ $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
+ }
+ catch
+ {
+
+ }
+ }
+ }
+ }
+}
+# SIG # Begin signature block
+# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDC68wb97fg0QGL
+# yXrxJhYfmibzcOh8caqC0uZprfczDaCCDYEwggX/MIID56ADAgECAhMzAAAB32vw
+# LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
+# bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw
+# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
+# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+# AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn
+# s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw
+# PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS
+# yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG
+# 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh
+# EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
+# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw
+# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
+# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu
+# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
+# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
+# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
+# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
+# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH
+# tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS
+# 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp
+# TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok
+# t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4
+# b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao
+# mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD
+# Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt
+# VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G
+# CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+
+# Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82
+# oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS
+# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
+# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
+# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
+# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
+# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
+# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
+# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
+# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
+# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
+# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
+# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
+# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
+# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
+# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
+# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
+# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
+# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
+# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
+# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
+# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
+# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
+# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
+# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
+# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
+# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
+# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
+# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
+# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
+# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
+# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
+# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
+# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
+# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
+# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
+# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
+# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
+# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
+# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
+# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
+# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG
+# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
+# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
+# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN
+# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
+# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgF1ypFyzl
+# AvvWGVCeXczrfpXmJNm9vpyjcwd4y4ivfqowQgYKKwYBBAGCNwIBDDE0MDKgFIAS
+# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
+# BgkqhkiG9w0BAQEFAASCAQBtnC8PmVgmkVoKR15F39ljf7fpP29Vlo9dnBmOcDAw
+# lHnt37zK9UIQSoyqiMhY/lt8iltz49NEj3D3ddXTXdIOXlfjUYvoIIrNUahgDF/Z
+# 3iQVhBW6Me8FEF4ImSntGkkvf86Hp5dlLCrpdDDWVVJkCxRGvCFXC0aNPHlFHRF1
+# Hbqqstm9xbYliNTk3BhJoo56j8XO61JkNEjzva3gemuG4dVhFSz9OF5HsjPTpTiJ
+# pg5//GDE5xeho5kwxk8Algyfac3vseJXLr6388cIP556sruynQumo0+K0cxyxhVI
+# cyvYlZAi4WzRpNNnWP8VXFb0ITFbgr0SLBIYUrQGFr2QoYIS8TCCEu0GCisGAQQB
+# gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME
+# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB
+# MDEwDQYJYIZIAWUDBAIBBQAEIJp8zyESzF9BGcJWXqSm1vrCJ/LFDEjr5Yc3y0OW
+# 46OzAgZgieX0wY0YEzIwMjEwNTEzMTkwNDA4Ljg1NlowBIACAfSggdSkgdEwgc4x
+# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
+# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p
+# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg
+# VFNTIEVTTjo0RDJGLUUzREQtQkVFRjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
+# U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABX8OuZVblU1jsAAAA
+# AAFfMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
+# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
+# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
+# MB4XDTIxMDExNDE5MDIxOVoXDTIyMDQxMTE5MDIxOVowgc4xCzAJBgNVBAYTAlVT
+# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
+# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy
+# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo0RDJG
+# LUUzREQtQkVFRjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj
+# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALw9efmC2WQ9uaaw7k4g
+# xHSSCEoJLk22FTAaF8jYbAMkQC6DQF0WPnIheLM1ERTuQ9FWbglf0mXbDd2KjezR
+# Nlz53ycJIReiGUQOnw5vd4TgjLUxL17g3K0MP2nNhY/LyP98Ml/40X905egDbiIn
+# dZdtHiDb1xfY17a7v1j9o3muc+MCgFt9fO+U4CDNUpMMMQJFr/9QlU4YdJawjbyK
+# fK3Ltvqfq3lvgK0/HphiDtX5ch3beGNBKowKSTXhft8pwuXQProutWgB5PZmAN8X
+# ZhACo4jWi/a0zgAJJcBqoXvS6InrWcH/Eqi/qVaj8Vs56/Z/6kaYZZu/1mSzLn5E
+# ALMCAwEAAaOCARswggEXMB0GA1UdDgQWBBQl7OnTlc0rgZ7Fd7qlDFguYTU49TAf
+# BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH
+# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU
+# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF
+# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0
+# YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG
+# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQAOgtfZLJYSbsE3W73nd0hLnqQqHSFl
+# 2spHxzeXxM4uJT2uAVk/SLVzzjvZemUDBeOedKeXG8hctprpoQMpU3gbsNUnUaDe
+# sDcmR+eELCwYa+VBkUCqsIGJmQlDwuDwNa67kyCEPyPW59Yu2w/djNrwNWSjtuRw
+# fUFoDkjYyDjnXD0josi67qxJgW8rRqjl9a62hGzlzgE+aVLTT5IhK5z2X62Lph8j
+# 9f4XjtCPnyeFKFmgBWHPY1HbbjUHfg91StCLxueH2LjZoQETWOJ+pxElicXwVP5B
+# 0wlWkiauwug3rTKnDb5WKUb2llsnQgaegV+MQjMI7K6v+spvsMgRjPlhMIIGcTCC
+# BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC
+# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
+# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv
+# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN
+# MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
+# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
+# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw
+# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0
+# VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw
+# RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe
+# dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx
+# Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G
+# kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA
+# AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7
+# fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
+# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
+# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
+# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
+# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
+# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g
+# AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93
+# d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB
+# BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA
+# bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh
+# IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS
+# +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK
+# kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon
+# /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi
+# PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/
+# fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII
+# YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0
+# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a
+# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ
+# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+
+# NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
+# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
+# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP
+# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo0
+# RDJGLUUzREQtQkVFRjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
+# dmljZaIjCgEBMAcGBSsOAwIaAxUA+gfSqjdAndOFEaXOQyBCdupmQoeggYMwgYCk
+# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
+# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
+# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
+# AORHga4wIhgPMjAyMTA1MTMxNDQ1MDJaGA8yMDIxMDUxNDE0NDUwMlowdzA9Bgor
+# BgEEAYRZCgQBMS8wLTAKAgUA5EeBrgIBADAKAgEAAgIVQwIB/zAHAgEAAgIRLTAK
+# AgUA5EjTLgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
+# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAHcp665K7IGKPBn3
+# +FtC8KgxAbGQbXp2oOCS8f+3Pu54iO/Ek4BR2F/YsanLXnr6nM/J1Qd9KVu8C6UJ
+# a41UfaEkEwbkLWBdEbr7bTbFReOfVlhObtYW2IrLXREmyeEgnce+7cGZZ0QLERSu
+# iQTNffmseSvEiARxDVXSpPsO3WsaMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC
+# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
+# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
+# bWUtU3RhbXAgUENBIDIwMTACEzMAAAFfw65lVuVTWOwAAAAAAV8wDQYJYIZIAWUD
+# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
+# CQQxIgQggG5jZDP+yZVGKxHthsrzCc0tL7YUokx1zgz/FJhsf0gwgfoGCyqGSIb3
+# DQEJEAIvMYHqMIHnMIHkMIG9BCDQzXq1KxGsLuj0szktrnlhIRqmbwp5bVGc6Bu6
+# hglMXDCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
+# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
+# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
+# X8OuZVblU1jsAAAAAAFfMCIEIPpiRGFlqZgftkQa9jNO7zYg4bdv1ZAveCzZoH8k
+# BxL1MA0GCSqGSIb3DQEBCwUABIIBAKgTDD8eJkOyyk9VEeHVmR8wisdcPHgu4vJa
+# yZx4290MVwWAZ8aFYnsrmocb1csIEFlKelbIeB2gJKrdFQwoLiTyN1suVC8mOToz
+# FICo6csyu9i5UTNYvidCZXaOZDom6cqamlCjA83npO+UERYvcldkiS3sjK8ejk01
+# OKwCMT8qxws2Csa3D/lm46ig5D4I0a5HccUiaoVMXk3RJtypvyutoH27pBAu+PhW
+# jwY0yW4YgS+ZaNgmlCSNkywUKzM3GHpVZd9hAmiIehr52FXIjtGHg6t5VOWlUVXT
+# CP5QCuqwUB4RxJUNJ1+yYuLCryZ0eYurv3Kw2yuTOvqyvVAyu9c=
+# SIG # End signature block
diff --git a/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs
new file mode 100644
index 0000000000..a346032365
--- /dev/null
+++ b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs
@@ -0,0 +1,750 @@
+namespace BenchmarkDotNet.Analyzers.Tests.AnalyzerTests.Attributes
+{
+ using Fixtures;
+
+ using Analyzers.Attributes;
+
+ using Xunit;
+
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Linq;
+ using System.Threading.Tasks;
+
+ public class ArgumentsAttributeAnalyzerTests
+ {
+ public class General : AnalyzerTestFixture
+ {
+ [Theory, CombinatorialData]
+ public async Task A_method_annotated_with_an_arguments_attribute_with_no_values_and_the_benchmark_attribute_and_having_no_parameters_should_not_trigger_diagnostic([CombinatorialMemberData(nameof(EmptyArgumentsAttributeUsages))] string emptyArgumentsAttributeUsage,
+ [CombinatorialRange(1, 2)] int attributeUsageMultiplier)
+ {
+ var emptyArgumentsAttributeUsages = new List();
+
+ for (var i = 0; i < attributeUsageMultiplier; i++)
+ {
+ emptyArgumentsAttributeUsages.Add(emptyArgumentsAttributeUsage);
+ }
+
+ var testCode = /* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [Benchmark]
+ {string.Join("\n", emptyArgumentsAttributeUsages)}
+ public void BenchmarkMethod()
+ {{
+
+ }}
+}}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ public static IEnumerable EmptyArgumentsAttributeUsages()
+ {
+ yield return "[Arguments]";
+ yield return "[Arguments()]";
+ yield return "[Arguments(Priority = 1)]";
+
+ var nameColonUsages = new List
+ {
+ "",
+ "values: "
+ };
+
+ var priorityNamedParameterUsages = new List
+ {
+ "",
+ ", Priority = 1"
+ };
+
+ var attributeUsagesBase = new List
+ {
+ "[Arguments({0}new object[] {{ }}{1})]",
+ "[Arguments({0}new object[0]{1})]",
+#if NET8_0_OR_GREATER
+ "[Arguments({0}[]{1})]",
+#endif
+ };
+
+ foreach (var attributeUsageBase in attributeUsagesBase)
+ {
+ foreach (var nameColonUsage in nameColonUsages)
+ {
+ foreach (var priorityNamedParameterUsage in priorityNamedParameterUsages)
+ {
+ yield return string.Format(attributeUsageBase, nameColonUsage, priorityNamedParameterUsage);
+ }
+ }
+ }
+ }
+ }
+
+ public class RequiresBenchmarkAttribute : AnalyzerTestFixture
+ {
+ public RequiresBenchmarkAttribute() : base(ArgumentsAttributeAnalyzer.RequiresBenchmarkAttributeRule) { }
+
+ [Theory]
+ [MemberData(nameof(ArgumentAttributeUsagesListLength))]
+ public async Task A_method_annotated_with_at_least_one_arguments_attribute_together_with_the_benchmark_attribute_should_not_trigger_diagnostic(int argumentAttributeUsagesListLength)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [Benchmark]
+ [{string.Join("]\n[", ArgumentAttributeUsages.Take(argumentAttributeUsagesListLength))}]
+ public void BenchmarkMethod()
+ {{
+
+ }}
+}}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(ArgumentAttributeUsagesListLength))]
+ public async Task A_method_with_at_least_one_arguments_attribute_but_no_benchmark_attribute_should_trigger_diagnostic(int argumentAttributeUsagesListLength)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ {string.Join("\n", ArgumentAttributeUsages.Take(argumentAttributeUsagesListLength).Select((a, i) => $"[{{|#{i}:{a}|}}]"))}
+ public void BenchmarkMethod()
+ {{
+
+ }}
+}}";
+
+ TestCode = testCode;
+
+ for (var i = 0; i < argumentAttributeUsagesListLength; i++)
+ {
+ AddExpectedDiagnostic(i);
+ }
+
+ await RunAsync();
+ }
+
+ public static TheoryData ArgumentAttributeUsagesListLength => new TheoryData(Enumerable.Range(1, ArgumentAttributeUsages.Count));
+
+ private static ReadOnlyCollection ArgumentAttributeUsages => new List {
+ "Arguments",
+ "Arguments()",
+ "Arguments(42, \"test\")"
+ }.AsReadOnly();
+ }
+
+ public class MethodWithoutAttributeMustHaveNoParameters : AnalyzerTestFixture
+ {
+ public MethodWithoutAttributeMustHaveNoParameters() : base(ArgumentsAttributeAnalyzer.MethodWithoutAttributeMustHaveNoParametersRule) { }
+
+ [Fact]
+ public async Task A_method_annotated_with_an_arguments_attribute_and_the_benchmark_attribute_and_having_parameters_should_not_trigger_diagnostic()
+ {
+ const string testCode =
+/* lang=c#-test */ @"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{
+ [Benchmark]
+ [Arguments(42, ""test"")]
+ public void BenchmarkMethod(int a, string b)
+ {
+
+ }
+}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(ParametersListLength))]
+ public async Task A_method_with_parameters_and_no_arguments_or_benchmark_attributes_should_not_trigger_diagnostic(int parametersListLength)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ public void BenchmarkMethod({string.Join(", ", Parameters.Take(parametersListLength))})
+ {{
+
+ }}
+}}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(ParametersListLength))]
+ public async Task A_method_annotated_with_the_benchmark_attribute_but_no_arguments_attribute_with_parameters_should_trigger_diagnostic(int parametersListLength)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [Benchmark]
+ public void BenchmarkMethod({{|#0:{string.Join(", ", Parameters.Take(parametersListLength))}|}})
+ {{
+
+ }}
+}}";
+
+ TestCode = testCode;
+ AddDefaultExpectedDiagnostic();
+
+ await RunAsync();
+ }
+
+ public static TheoryData ParametersListLength => new TheoryData(Enumerable.Range(1, Parameters.Count));
+
+ private static ReadOnlyCollection Parameters => new List {
+ "int a",
+ "string b",
+ "bool c"
+ }.AsReadOnly();
+ }
+
+ public class MustHaveMatchingValueCount : AnalyzerTestFixture
+ {
+ public MustHaveMatchingValueCount() : base(ArgumentsAttributeAnalyzer.MustHaveMatchingValueCountRule) { }
+
+ [Fact]
+ public async Task A_method_not_annotated_with_any_arguments_attributes_should_not_trigger_diagnostic()
+ {
+ const string testCode =
+/* lang=c#-test */ @"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{
+ [Benchmark]
+ public void BenchmarkMethod()
+ {
+
+ }
+}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(ArgumentsAttributeUsages))]
+ public async Task Having_a_matching_value_count_should_not_trigger_diagnostic(string argumentsAttributeUsage)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [Benchmark]
+ {argumentsAttributeUsage}
+ public void BenchmarkMethod(string a, bool b)
+ {{
+
+ }}
+}}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(EmptyArgumentsAttributeUsagesWithLocationMarker))]
+ public async Task Having_a_mismatching_empty_value_count_targeting_a_method_with_parameters_should_trigger_diagnostic(string argumentsAttributeUsage)
+ {
+ const string benchmarkMethodName = "BenchmarkMethod";
+
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [Benchmark]
+ {argumentsAttributeUsage}
+ public void {benchmarkMethodName}(string a)
+ {{
+
+ }}
+}}";
+ TestCode = testCode;
+ AddDefaultExpectedDiagnostic(1, "", benchmarkMethodName, 0);
+
+ await RunAsync();
+ }
+
+ [Theory, CombinatorialData]
+ public async Task Having_a_mismatching_value_count_should_trigger_diagnostic([CombinatorialMemberData(nameof(ArgumentsAttributeUsagesWithLocationMarker))] string argumentsAttributeUsage,
+ [CombinatorialMemberData(nameof(ParameterLists))] (string Parameters, int ParameterCount, string PluralSuffix) parameterData)
+ {
+ const string benchmarkMethodName = "BenchmarkMethod";
+
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [Benchmark]
+ {argumentsAttributeUsage}
+ public void {benchmarkMethodName}({parameterData.Parameters})
+ {{
+
+ }}
+}}";
+ TestCode = testCode;
+ AddExpectedDiagnostic(0, parameterData.ParameterCount, parameterData.PluralSuffix, benchmarkMethodName, 2);
+ AddExpectedDiagnostic(1, parameterData.ParameterCount, parameterData.PluralSuffix, benchmarkMethodName, 3);
+
+ await RunAsync();
+ }
+
+ public static IEnumerable<(string, int, string)> ParameterLists => new [] { ("string a", 1, ""), ("", 0, "s") };
+
+ public static TheoryData ArgumentsAttributeUsages()
+ {
+ return new TheoryData(GenerateData());
+
+#if NET6_0_OR_GREATER
+ static IEnumerable GenerateData()
+#else
+ IEnumerable GenerateData()
+#endif
+ {
+ var nameColonUsages = new List
+ {
+ "",
+ "values: "
+ };
+
+ var priorityNamedParameterUsages = new List
+ {
+ "",
+ ", Priority = 1"
+ };
+
+ var attributeUsagesBase = new List
+ {
+ "[Arguments({1}{2})]",
+ "[Arguments({0}new object[] {{ {1} }}{2})]",
+#if NET8_0_OR_GREATER
+ "[Arguments({0}[ {1} ]{2})]"
+#endif
+ };
+
+ var valueLists = new List
+ {
+ "42, \"test\"",
+ "\"value\", 100"
+ };
+
+ foreach (var attributeUsageBase in attributeUsagesBase)
+ {
+ foreach (var nameColonUsage in nameColonUsages)
+ {
+ foreach (var priorityNamedParameterUsage in priorityNamedParameterUsages)
+ {
+ yield return string.Join("\n ", valueLists.Select(vv => string.Format(attributeUsageBase, nameColonUsage, vv, priorityNamedParameterUsage)));
+ }
+ }
+ }
+ }
+ }
+
+ public static TheoryData EmptyArgumentsAttributeUsagesWithLocationMarker()
+ {
+ return new TheoryData(GenerateData());
+
+#if NET6_0_OR_GREATER
+ static IEnumerable GenerateData()
+#else
+ IEnumerable GenerateData()
+#endif
+ {
+ yield return "[{|#0:Arguments|}]";
+ yield return "[Arguments{|#0:()|}]";
+ yield return "[Arguments({|#0:Priority = 1|})]";
+
+ var nameColonUsages = new List
+ {
+ "",
+ "values: "
+ };
+
+ var priorityNamedParameterUsages = new List
+ {
+ "",
+ ", Priority = 1"
+ };
+
+ var attributeUsagesBase = new List
+ {
+ "[Arguments({0}new object[] {{|#0:{{ }}|}}{1})]",
+ "[Arguments({0}new object[{{|#0:0|}}]{1})]",
+#if NET8_0_OR_GREATER
+ "[Arguments({0}{{|#0:[]|}}{1})]",
+#endif
+ };
+
+ foreach (var attributeUsageBase in attributeUsagesBase)
+ {
+ foreach (var nameColonUsage in nameColonUsages)
+ {
+ foreach (var priorityNamedParameterUsage in priorityNamedParameterUsages)
+ {
+ yield return string.Format(attributeUsageBase, nameColonUsage, priorityNamedParameterUsage);
+ }
+ }
+ }
+ }
+ }
+
+ public static IEnumerable ArgumentsAttributeUsagesWithLocationMarker()
+ {
+ var nameColonUsages = new List
+ {
+ "",
+ "values: "
+ };
+
+ var priorityNamedParameterUsages = new List
+ {
+ "",
+ ", Priority = 1"
+ };
+
+ var attributeUsagesBase = new List
+ {
+ "[Arguments({{|#{1}:{2}|}}{3})]",
+ "[Arguments({0}new object[] {{ {{|#{1}:{2}|}} }}{3})]",
+#if NET8_0_OR_GREATER
+ "[Arguments({0}[ {{|#{1}:{2}|}} ]{3})]"
+#endif
+ };
+
+ var valueLists = new List
+ {
+ "42, \"test\"",
+ "\"value\", 100, false"
+ };
+
+ foreach (var attributeUsageBase in attributeUsagesBase)
+ {
+ foreach (var nameColonUsage in nameColonUsages)
+ {
+ foreach (var priorityNamedParameterUsage in priorityNamedParameterUsages)
+ {
+ yield return string.Join("\n ", valueLists.Select((vv, i) => string.Format(attributeUsageBase, nameColonUsage, i, vv, priorityNamedParameterUsage)));
+ }
+ }
+ }
+ }
+ }
+
+ public class MustHaveMatchingValueType : AnalyzerTestFixture
+ {
+ public MustHaveMatchingValueType() : base(ArgumentsAttributeAnalyzer.MustHaveMatchingValueTypeRule) { }
+
+ [Fact]
+ public async Task A_method_not_annotated_with_any_arguments_attributes_should_not_trigger_diagnostic()
+ {
+ const string testCode =
+/* lang=c#-test */ @"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{
+ [Benchmark]
+ public void BenchmarkMethod()
+ {
+
+ }
+}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(EmptyArgumentsAttributeUsagesWithMismatchingValueCount))]
+ public async Task Having_a_mismatching_value_count_with_empty_argument_attribute_usages_should_not_trigger_diagnostic(string argumentsAttributeUsage)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+public class BenchmarkClass
+{{
+ [Benchmark]
+ {argumentsAttributeUsage}
+ public void BenchmarkMethod(string a)
+ {{
+
+ }}
+}}";
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory, CombinatorialData]
+ public async Task Having_a_mismatching_value_count_with_nonempty_argument_attribute_usages_should_not_trigger_diagnostic([CombinatorialMemberData(nameof(ArgumentsAttributeUsagesWithMismatchingValueCount))] string argumentsAttributeUsage,
+ [CombinatorialValues("string a", "")] string parameters)
+ {
+ var testCode = /* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+public class BenchmarkClass
+{{
+ [Benchmark]
+ {argumentsAttributeUsage}
+ public void BenchmarkMethod({parameters})
+ {{
+
+ }}
+}}";
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(ArgumentsAttributeUsagesWithMatchingValueTypes))]
+ public async Task Having_matching_value_types_should_not_trigger_diagnostic(string argumentsAttributeUsage)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [Benchmark]
+ {argumentsAttributeUsage}
+ public void BenchmarkMethod(int a, string b)
+ {{
+
+ }}
+}}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(ArgumentsAttributeUsagesWithConvertibleValueTypes))]
+ public async Task Providing_convertible_value_types_should_not_trigger_diagnostic(string argumentsAttributeUsage)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [Benchmark]
+ {argumentsAttributeUsage}
+ public void BenchmarkMethod(int a, string b)
+ {{
+
+ }}
+}}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(ArgumentsAttributeUsagesWithMatchingValueTypes))]
+ public async Task Having_unknown_parameter_type_should_not_trigger_diagnostic(string argumentsAttributeUsage)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [Benchmark]
+ {argumentsAttributeUsage}
+ public void BenchmarkMethod(unkown a, string b)
+ {{
+
+ }}
+}}";
+
+ TestCode = testCode;
+
+ DisableCompilerDiagnostics();
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(ArgumentsAttributeUsagesWithMismatchingValueTypesWithLocationMarker))]
+ public async Task Having_mismatching_or_not_convertible_value_types_should_trigger_diagnostic(string argumentsAttributeUsage)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [Benchmark]
+ {argumentsAttributeUsage}
+ public void BenchmarkMethod(byte a, bool b)
+ {{
+
+ }}
+}}";
+
+ TestCode = testCode;
+
+ AddExpectedDiagnostic(0, "typeof(string)", "byte", "System.Type");
+ AddExpectedDiagnostic(1, "\"test\"", "bool", "string");
+ AddExpectedDiagnostic(2, "999", "byte", "int");
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(ArgumentsAttributeUsagesWithUnknownValueTypesWithLocationMarker))]
+ public async Task Providing_an_unkown_value_type_should_trigger_diagnostic(string argumentsAttributeUsage)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [Benchmark]
+ {argumentsAttributeUsage}
+ public void BenchmarkMethod(byte a, bool b)
+ {{
+
+ }}
+}}";
+
+ TestCode = testCode;
+
+ DisableCompilerDiagnostics();
+ AddDefaultExpectedDiagnostic("dummy_literal", "byte", "");
+
+ await RunAsync();
+ }
+
+ public static TheoryData EmptyArgumentsAttributeUsagesWithMismatchingValueCount()
+ {
+ return new TheoryData(GenerateData());
+
+#if NET6_0_OR_GREATER
+ static IEnumerable GenerateData()
+#else
+ IEnumerable GenerateData()
+#endif
+ {
+ yield return "[Arguments]";
+ yield return "[Arguments()]";
+ yield return "[Arguments(Priority = 1)]";
+
+ var nameColonUsages = new List
+ {
+ "",
+ "values: "
+ };
+
+ var priorityNamedParameterUsages = new List
+ {
+ "",
+ ", Priority = 1"
+ };
+
+ var attributeUsagesBase = new List
+ {
+ "[Arguments({0}new object[] {{ }}{1})]",
+ "[Arguments({0}new object[0]{1})]",
+#if NET8_0_OR_GREATER
+ "[Arguments({0}[]{1})]",
+#endif
+ };
+
+ foreach (var attributeUsageBase in attributeUsagesBase)
+ {
+ foreach (var nameColonUsage in nameColonUsages)
+ {
+ foreach (var priorityNamedParameterUsage in priorityNamedParameterUsages)
+ {
+ yield return string.Format(attributeUsageBase, nameColonUsage, priorityNamedParameterUsage);
+ }
+ }
+ }
+ }
+ }
+
+ public static IEnumerable ArgumentsAttributeUsagesWithMismatchingValueCount() => GenerateAttributeUsages(new List {
+ "42, \"test\"",
+ "\"value\", 100, true"
+ });
+
+ public static TheoryData ArgumentsAttributeUsagesWithMatchingValueTypes() => new TheoryData(GenerateAttributeUsages(new List {
+ "42, \"test\"",
+ "43, \"test2\""
+ }));
+
+ public static TheoryData ArgumentsAttributeUsagesWithConvertibleValueTypes() => new TheoryData(GenerateAttributeUsages(new List {
+ "42, \"test\"",
+ "(byte)5, \"test2\""
+ }));
+
+ public static TheoryData ArgumentsAttributeUsagesWithMismatchingValueTypesWithLocationMarker() => new TheoryData(GenerateAttributeUsages(new List {
+ "{|#0:typeof(string)|}, {|#1:\"test\"|}",
+ "{|#2:999|}, true"
+ }));
+
+ public static TheoryData ArgumentsAttributeUsagesWithUnknownValueTypesWithLocationMarker() => new TheoryData(GenerateAttributeUsages(new List {
+ "{|#0:dummy_literal|}, true"
+ }));
+ }
+
+ private static IEnumerable GenerateAttributeUsages(List valueLists)
+ {
+ var nameColonUsages = new List
+ {
+ "",
+ "values: "
+ };
+
+ var priorityNamedParameterUsages = new List
+ {
+ "",
+ ", Priority = 1"
+ };
+
+ var attributeUsagesBase = new List
+ {
+ "[Arguments({1}{2})]",
+ "[Arguments({0}new object[] {{ {1} }}{2})]",
+#if NET8_0_OR_GREATER
+ "[Arguments({0}[ {1} ]{2})]"
+#endif
+ };
+
+ foreach (var attributeUsageBase in attributeUsagesBase)
+ {
+ foreach (var nameColonUsage in nameColonUsages)
+ {
+ foreach (var priorityNamedParameterUsage in priorityNamedParameterUsages)
+ {
+ yield return string.Join("\n ", valueLists.Select(vv => string.Format(attributeUsageBase, nameColonUsage, vv, priorityNamedParameterUsage)));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/GeneralParameterAttributesAnalyzerTests.cs b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/GeneralParameterAttributesAnalyzerTests.cs
new file mode 100644
index 0000000000..e116912a56
--- /dev/null
+++ b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/GeneralParameterAttributesAnalyzerTests.cs
@@ -0,0 +1,1332 @@
+namespace BenchmarkDotNet.Analyzers.Tests.AnalyzerTests.Attributes
+{
+ using Fixtures;
+
+ using Analyzers.Attributes;
+
+ using Xunit;
+
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Linq;
+ using System.Threading.Tasks;
+
+ public class GeneralParameterAttributesAnalyzerTests
+ {
+ public class MutuallyExclusiveOnField : AnalyzerTestFixture
+ {
+ public MutuallyExclusiveOnField() : base(GeneralParameterAttributesAnalyzer.MutuallyExclusiveOnFieldRule) { }
+
+ [Fact]
+ public async Task A_field_not_annotated_with_any_parameter_attribute_should_not_trigger_diagnostic()
+ {
+ const string testCode =
+/* lang=c#-test */ @"public class BenchmarkClass
+{
+ public int _field = 0, _field2 = 1;
+}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Fact]
+ public async Task A_field_annotated_with_a_nonparameter_attribute_should_not_trigger_diagnostic()
+ {
+ const string testCode =
+/* lang=c#-test */ @"public class BenchmarkClass
+{
+ [Dummy]
+ public int _field = 0, _field2 = 1;
+}";
+
+ TestCode = testCode;
+ ReferenceDummyAttribute();
+
+ await RunAsync();
+ }
+
+ [Fact]
+ public async Task A_field_annotated_with_a_duplicate_nonparameter_attribute_should_not_trigger_diagnostic()
+ {
+ const string testCode
+= /* lang=c#-test */ @"public class BenchmarkClass
+{
+ [Dummy]
+ [Dummy]
+ public int _field = 0, _field2 = 1;
+}";
+
+ TestCode = testCode;
+ ReferenceDummyAttribute();
+ DisableCompilerDiagnostics();
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(UniqueParameterAttributeUsages))]
+ public async Task A_field_annotated_with_a_unique_parameter_attribute_should_not_trigger_diagnostic(string attributeUsage)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [{attributeUsage}]
+ public int _field = 0, _field2 = 1;
+}}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(DuplicateSameParameterAttributeUsages))]
+ public async Task A_field_annotated_with_the_same_duplicate_parameter_attribute_should_not_trigger_diagnostic(string currentUniqueAttributeUsage, int currentUniqueAttributeUsagePosition, int[] counts)
+ {
+ var duplicateAttributeUsages = new List(1 + counts.Sum());
+
+ var uniqueParameterAttributeUsages = UniqueParameterAttributeUsages.AsReadOnly();
+
+ for (var i = 0; i < counts.Length; i++)
+ {
+ if (i == currentUniqueAttributeUsagePosition)
+ {
+ duplicateAttributeUsages.Add($"[{currentUniqueAttributeUsage}]");
+ }
+
+ for (var j = 0; j < counts[i]; j++)
+ {
+ duplicateAttributeUsages.Add($"[{uniqueParameterAttributeUsages[i]}]");
+ }
+ }
+
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ {string.Join($"{Environment.NewLine} ", duplicateAttributeUsages)}
+ public int _field = 0, _field2 = 1;
+}}";
+
+ TestCode = testCode;
+ DisableCompilerDiagnostics();
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(DuplicateParameterAttributeUsageCounts))]
+ public async Task A_field_annotated_with_more_than_one_parameter_attribute_should_trigger_diagnostic_for_each_attribute_usage(int[] duplicateAttributeUsageCounts)
+ {
+ const string fieldIdentifier = "_field";
+
+ var duplicateAttributeUsages = new List(duplicateAttributeUsageCounts.Sum());
+
+ var uniqueParameterAttributeUsages = UniqueParameterAttributeUsages.AsReadOnly();
+
+ var diagnosticCounter = 0;
+ for (var i = 0; i < duplicateAttributeUsageCounts.Length; i++)
+ {
+ for (var j = 0; j < duplicateAttributeUsageCounts[i]; j++)
+ {
+ duplicateAttributeUsages.Add($"[{{|#{diagnosticCounter++}:{uniqueParameterAttributeUsages[i]}|}}]");
+ }
+ }
+
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ {string.Join($"{Environment.NewLine} ", duplicateAttributeUsages)}
+ public int {fieldIdentifier} = 0, field2 = 1;
+}}";
+
+ TestCode = testCode;
+
+ for (var i = 0; i < diagnosticCounter; i++)
+ {
+ AddExpectedDiagnostic(i, fieldIdentifier);
+ }
+
+ await RunAsync();
+ }
+
+#if NET6_0_OR_GREATER
+ public static TheoryData UniqueParameterAttributeUsages => new(UniqueParameterAttributesTheoryData.Select(tdr => (tdr[1] as string)!));
+#else
+ public static TheoryData UniqueParameterAttributeUsages => new TheoryData(UniqueParameterAttributesTheoryData.Select(tdr => tdr[1] as string));
+#endif
+
+ public static TheoryData DuplicateSameParameterAttributeUsages => DuplicateSameAttributeUsagesTheoryData;
+
+ public static TheoryData DuplicateParameterAttributeUsageCounts => DuplicateAttributeUsageCountsTheoryData;
+ }
+
+ public class MutuallyExclusiveOnProperty : AnalyzerTestFixture
+ {
+ public MutuallyExclusiveOnProperty() : base(GeneralParameterAttributesAnalyzer.MutuallyExclusiveOnPropertyRule) { }
+
+ [Fact]
+ public async Task A_property_not_annotated_with_any_parameter_attribute_should_not_trigger_diagnostic()
+ {
+ const string testCode =
+/* lang=c#-test */ @"public class BenchmarkClass
+{
+ public int Property { get; set; }
+}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Fact]
+ public async Task A_property_annotated_with_a_nonparameter_attribute_should_not_trigger_diagnostic()
+ {
+ const string testCode =
+/* lang=c#-test */ @"public class BenchmarkClass
+{
+ [Dummy]
+ public int Property { get; set; }
+}";
+
+ TestCode = testCode;
+ ReferenceDummyAttribute();
+
+ await RunAsync();
+ }
+
+ [Fact]
+ public async Task A_property_annotated_with_a_duplicate_nonparameter_attribute_should_not_trigger_diagnostic()
+ {
+ const string testCode =
+/* lang=c#-test */ @"public class BenchmarkClass
+{
+ [Dummy]
+ [Dummy]
+ public int Property { get; set; }
+}";
+
+ TestCode = testCode;
+ ReferenceDummyAttribute();
+ DisableCompilerDiagnostics();
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(UniqueParameterAttributeUsages))]
+ public async Task A_property_annotated_with_a_unique_parameter_attribute_should_not_trigger_diagnostic(string attributeUsage)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [{attributeUsage}]
+ public int Property {{ get; set; }}
+}}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(DuplicateSameParameterAttributeUsages))]
+ public async Task A_property_annotated_with_the_same_duplicate_parameter_attribute_should_not_trigger_diagnostic(string currentAttributeUsage, int currentUniqueAttributeUsagePosition, int[] duplicateSameAttributeUsageCounts)
+ {
+ var duplicateAttributeUsages = new List(1 + duplicateSameAttributeUsageCounts.Sum());
+
+ var uniqueParameterAttributeUsages = UniqueParameterAttributeUsages.AsReadOnly();
+
+ for (var i = 0; i < duplicateSameAttributeUsageCounts.Length; i++)
+ {
+ if (i == currentUniqueAttributeUsagePosition)
+ {
+ duplicateAttributeUsages.Add($"[{currentAttributeUsage}]");
+ }
+
+ for (var j = 0; j < duplicateSameAttributeUsageCounts[i]; j++)
+ {
+ duplicateAttributeUsages.Add($"[{uniqueParameterAttributeUsages[i]}]");
+ }
+ }
+
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ {string.Join($"{Environment.NewLine} ", duplicateAttributeUsages)}
+ public int Property {{ get; set; }}
+}}";
+
+ TestCode = testCode;
+ DisableCompilerDiagnostics();
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(DuplicateParameterAttributeUsages))]
+ public async Task A_property_annotated_with_more_than_one_parameter_attribute_should_trigger_diagnostic_for_each_attribute_usage(int[] duplicateAttributeUsageCounts)
+ {
+ const string propertyIdentifier = "Property";
+
+ var duplicateAttributeUsages = new List(duplicateAttributeUsageCounts.Sum());
+
+ var uniqueParameterAttributeUsages = UniqueParameterAttributeUsages.AsReadOnly();
+
+ var diagnosticCounter = 0;
+ for (var i = 0; i < duplicateAttributeUsageCounts.Length; i++)
+ {
+ for (var j = 0; j < duplicateAttributeUsageCounts[i]; j++)
+ {
+ duplicateAttributeUsages.Add($"[{{|#{diagnosticCounter++}:{uniqueParameterAttributeUsages[i]}|}}]");
+ }
+ }
+
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ {string.Join($"{Environment.NewLine} ", duplicateAttributeUsages)}
+ public int {propertyIdentifier} {{ get; set; }}
+}}";
+
+ TestCode = testCode;
+
+ for (var i = 0; i < diagnosticCounter; i++)
+ {
+ AddExpectedDiagnostic(i, propertyIdentifier);
+ }
+
+ await RunAsync();
+ }
+
+#if NET6_0_OR_GREATER
+ public static TheoryData UniqueParameterAttributeUsages => new(UniqueParameterAttributesTheoryData.Select(tdr => (tdr[1] as string)!));
+#else
+ public static TheoryData UniqueParameterAttributeUsages => new TheoryData(UniqueParameterAttributesTheoryData.Select(tdr => tdr[1] as string));
+#endif
+
+ public static TheoryData DuplicateSameParameterAttributeUsages => DuplicateSameAttributeUsagesTheoryData;
+
+ public static TheoryData DuplicateParameterAttributeUsages => DuplicateAttributeUsageCountsTheoryData;
+ }
+
+ public class FieldMustBePublic : AnalyzerTestFixture
+ {
+ public FieldMustBePublic() : base(GeneralParameterAttributesAnalyzer.FieldMustBePublic) { }
+
+ [Theory]
+ [ClassData(typeof(NonPublicClassMemberAccessModifiersTheoryData))]
+ public async Task A_nonpublic_field_not_annotated_with_any_parameter_attribute_should_not_trigger_diagnostic(string classMemberAccessModifier)
+ {
+ var testCode =
+/* lang=c#-test */ $@"public class BenchmarkClass
+{{
+ {classMemberAccessModifier}int _field = 0, _field2 = 1;
+}}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [ClassData(typeof(NonPublicClassMemberAccessModifiersTheoryData))]
+ public async Task A_nonpublic_field_annotated_with_a_nonparameter_attribute_should_not_trigger_diagnostic(string classMemberAccessModifier)
+ {
+ var testCode =
+/* lang=c#-test */ $@"public class BenchmarkClass
+{{
+ [Dummy]
+ {classMemberAccessModifier}int _field = 0, _field2 = 1;
+}}";
+
+ TestCode = testCode;
+ ReferenceDummyAttribute();
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(UniqueParameterAttributeUsages))]
+ public async Task A_public_field_annotated_with_a_unique_parameter_attribute_should_not_trigger_diagnostic(string attributeUsage)
+ {
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [{attributeUsage}]
+ public int _field = 0, _field2 = 2;
+}}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory, CombinatorialData]
+ public async Task A_nonpublic_field_annotated_with_the_same_duplicate_parameter_attribute_should_not_trigger_diagnostic([CombinatorialMemberData(nameof(DuplicateSameParameterAttributeUsages))] (string CurrentUniqueAttributeUsage, int CurrentUniqueAttributeUsagePosition, int[] Counts) duplicateSameParameterAttributeUsages,
+ [CombinatorialMemberData(nameof(NonPublicClassMemberAccessModifiers))] string classMemberAccessModifier)
+ {
+ var duplicateAttributeUsages = new List(1 + duplicateSameParameterAttributeUsages.Counts.Sum());
+
+ var uniqueParameterAttributeUsages = UniqueParameterAttributeUsages.AsReadOnly();
+
+ for (var i = 0; i < duplicateSameParameterAttributeUsages.Counts.Length; i++)
+ {
+ if (i == duplicateSameParameterAttributeUsages.CurrentUniqueAttributeUsagePosition)
+ {
+ duplicateAttributeUsages.Add($"[{duplicateSameParameterAttributeUsages.CurrentUniqueAttributeUsage}]");
+ }
+
+ for (var j = 0; j < duplicateSameParameterAttributeUsages.Counts[i]; j++)
+ {
+ duplicateAttributeUsages.Add($"[{uniqueParameterAttributeUsages[i]}]");
+ }
+ }
+
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ {string.Join($"{Environment.NewLine} ", duplicateAttributeUsages)}
+ {classMemberAccessModifier}int _field = 0, _field2 = 1;
+}}";
+
+ TestCode = testCode;
+ DisableCompilerDiagnostics();
+
+ await RunAsync();
+ }
+
+ [Theory]
+ [MemberData(nameof(DuplicateAttributeUsageCountsAndNonPublicClassMemberAccessModifiersCombinations))]
+ public async Task A_nonpublic_field_annotated_with_more_than_one_parameter_attribute_should_not_trigger_diagnostic(int[] duplicateAttributeUsageCounts, string classMemberAccessModifier)
+ {
+ var duplicateAttributeUsages = new List(duplicateAttributeUsageCounts.Sum());
+
+ var uniqueParameterAttributeUsages = UniqueParameterAttributeUsages.AsReadOnly();
+
+ for (var i = 0; i < duplicateAttributeUsageCounts.Length; i++)
+ {
+ for (var j = 0; j < duplicateAttributeUsageCounts[i]; j++)
+ {
+ duplicateAttributeUsages.Add($"[{uniqueParameterAttributeUsages[i]}]");
+ }
+ }
+
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ {string.Join($"{Environment.NewLine} ", duplicateAttributeUsages)}
+ {classMemberAccessModifier}int _field = 0, _field2 = 1;
+}}";
+
+ TestCode = testCode;
+
+ await RunAsync();
+ }
+
+ [Theory, CombinatorialData]
+ public async Task A_nonpublic_field_annotated_with_a_unique_parameter_attribute_should_trigger_diagnostic([CombinatorialMemberData(nameof(UniqueParameterAttributes))] (string AttributeName, string AttributeUsage) attribute,
+ [CombinatorialMemberData(nameof(NonPublicClassMemberAccessModifiers))] string classMemberAccessModifier)
+ {
+ const string fieldIdentifier = "_field";
+
+ var testCode =
+/* lang=c#-test */ $@"using BenchmarkDotNet.Attributes;
+
+public class BenchmarkClass
+{{
+ [{attribute.AttributeUsage}]
+ {classMemberAccessModifier}int {{|#0:{fieldIdentifier}|}} = 0, field2 = 0;
+}}";
+ TestCode = testCode;
+ AddDefaultExpectedDiagnostic(fieldIdentifier, attribute.AttributeName);
+
+ await RunAsync();
+ }
+
+ public static IEnumerable