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..2398ad7d66
--- /dev/null
+++ b/BenchmarkDotNet.Analyzers.sln
@@ -0,0 +1,43 @@
+
+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", "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
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Analyzers", "src\BenchmarkDotNet.Analyzers\BenchmarkDotNet.Analyzers.csproj", "{AA4DDCA0-C1D8-ADA8-69FE-2F67C4CA96B1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Analyzers.Tests", "tests\BenchmarkDotNet.Analyzers.Tests\BenchmarkDotNet.Analyzers.Tests.csproj", "{7DE89F16-2160-42E3-004E-1F5064732121}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {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
+ {AA4DDCA0-C1D8-ADA8-69FE-2F67C4CA96B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AA4DDCA0-C1D8-ADA8-69FE-2F67C4CA96B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AA4DDCA0-C1D8-ADA8-69FE-2F67C4CA96B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AA4DDCA0-C1D8-ADA8-69FE-2F67C4CA96B1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7DE89F16-2160-42E3-004E-1F5064732121}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7DE89F16-2160-42E3-004E-1F5064732121}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7DE89F16-2160-42E3-004E-1F5064732121}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7DE89F16-2160-42E3-004E-1F5064732121}.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/build/common.props b/build/common.props
index bb8b1ad253..c22f8f841c 100644
--- a/build/common.props
+++ b/build/common.props
@@ -21,7 +21,7 @@
false
$(MSBuildThisFileDirectory)CodingStyle.ruleset
true
-
+ NU1900
annotations
true
diff --git a/src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs b/src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs
new file mode 100644
index 0000000000..597181715a
--- /dev/null
+++ b/src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs
@@ -0,0 +1,205 @@
+namespace BenchmarkDotNet.Analyzers
+{
+ using Microsoft.CodeAnalysis;
+ using Microsoft.CodeAnalysis.CSharp;
+ using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+ using System.Collections.Immutable;
+ using System.Globalization;
+ using System.Linq;
+
+ internal static class AnalyzerHelper
+ {
+ public static LocalizableResourceString GetResourceString(string name) => new(name, BenchmarkDotNetAnalyzerResources.ResourceManager, typeof(BenchmarkDotNetAnalyzerResources));
+
+ public static INamedTypeSymbol? GetBenchmarkAttributeTypeSymbol(Compilation compilation) => compilation.GetTypeByMetadataName("BenchmarkDotNet.Attributes.BenchmarkAttribute");
+
+ public static bool AttributeListsContainAttribute(string attributeName, Compilation compilation, SyntaxList attributeLists, SemanticModel semanticModel) => AttributeListsContainAttribute(compilation.GetTypeByMetadataName(attributeName), attributeLists, semanticModel);
+
+ public static bool AttributeListsContainAttribute(INamedTypeSymbol? attributeTypeSymbol, SyntaxList attributeLists, SemanticModel semanticModel)
+ {
+ if (attributeTypeSymbol == null || attributeTypeSymbol.TypeKind == TypeKind.Error)
+ {
+ return false;
+ }
+
+ foreach (var attributeListSyntax in attributeLists)
+ {
+ foreach (var attributeSyntax in attributeListSyntax.Attributes)
+ {
+ var attributeSyntaxTypeSymbol = semanticModel.GetTypeInfo(attributeSyntax).Type;
+ if (attributeSyntaxTypeSymbol == null)
+ {
+ continue;
+ }
+
+ if (attributeSyntaxTypeSymbol.Equals(attributeTypeSymbol, SymbolEqualityComparer.Default))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public static bool AttributeListContainsAttribute(string attributeName, Compilation compilation, ImmutableArray attributeList) => AttributeListContainsAttribute(compilation.GetTypeByMetadataName(attributeName), attributeList);
+
+ public static bool AttributeListContainsAttribute(INamedTypeSymbol? attributeTypeSymbol, ImmutableArray attributeList)
+ {
+ if (attributeTypeSymbol == null || attributeTypeSymbol.TypeKind == TypeKind.Error)
+ {
+ return false;
+ }
+
+ return attributeList.Any(ad => ad.AttributeClass != null && ad.AttributeClass.Equals(attributeTypeSymbol, SymbolEqualityComparer.Default));
+ }
+
+ public static ImmutableArray GetAttributes(string attributeName, Compilation compilation, SyntaxList attributeLists, SemanticModel semanticModel) => GetAttributes(compilation.GetTypeByMetadataName(attributeName), attributeLists, semanticModel);
+
+ public static ImmutableArray GetAttributes(INamedTypeSymbol? attributeTypeSymbol, SyntaxList attributeLists, SemanticModel semanticModel)
+ {
+ var attributesBuilder = ImmutableArray.CreateBuilder();
+
+ if (attributeTypeSymbol == null)
+ {
+ return attributesBuilder.ToImmutable();
+ }
+
+ foreach (var attributeListSyntax in attributeLists)
+ {
+ foreach (var attributeSyntax in attributeListSyntax.Attributes)
+ {
+ var attributeSyntaxTypeSymbol = semanticModel.GetTypeInfo(attributeSyntax).Type;
+ if (attributeSyntaxTypeSymbol == null)
+ {
+ continue;
+ }
+
+ if (attributeSyntaxTypeSymbol.Equals(attributeTypeSymbol, SymbolEqualityComparer.Default))
+ {
+ attributesBuilder.Add(attributeSyntax);
+ }
+ }
+ }
+
+ return attributesBuilder.ToImmutable();
+ }
+
+ public static string NormalizeTypeName(INamedTypeSymbol namedTypeSymbol)
+ {
+ string typeName;
+
+ if (namedTypeSymbol.SpecialType != SpecialType.None)
+ {
+ typeName = namedTypeSymbol.ToString();
+ }
+ else if (namedTypeSymbol.IsUnboundGenericType)
+ {
+ typeName = $"{namedTypeSymbol.Name}<{new string(',', namedTypeSymbol.TypeArguments.Length - 1)}>";
+ }
+ else
+ {
+ typeName = namedTypeSymbol.Name;
+ }
+
+ return typeName;
+ }
+
+ public static bool IsAssignableToField(Compilation compilation, ITypeSymbol targetType, string valueExpression, Optional