Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e0bd1d6
Add Roslyn analyzers to detect incorrect usage of BenchmarkDotNet
silkfire Sep 26, 2025
c18a417
Unify C# language version
silkfire Oct 10, 2025
19e6ff9
Remove Analyzers package projects
silkfire Oct 10, 2025
58ce544
Revert BenchmarkDotNet.Disassembler changes
silkfire Oct 10, 2025
86d3a9c
Reference Analyzers project from Annotations
silkfire Oct 10, 2025
770e190
Move Benchmark.Analyzers and Benchmark.Analyzers.Tests to correct dir…
silkfire Oct 10, 2025
9d2423c
Remove accidentally added package Microsoft.CodeAnalysis.NetAnalyzers…
silkfire Oct 11, 2025
6719209
* Benchmark classes annotated with a [GenericTypeArguments] attribute…
silkfire Oct 12, 2025
4292904
* Change diagnostic ID increment ordering
silkfire Oct 13, 2025
a8dab86
When determining whether a class has any benchmark methods, iterate t…
silkfire Oct 13, 2025
4ab0b37
Move "Benchmark class cannot be sealed" to Run analyzer
silkfire Oct 13, 2025
57df116
Move "Benchmark class must be public" to Run analyzer
silkfire Oct 13, 2025
c576530
Support analyzing overload of BenchmarkRunner.Run that takes a Type p…
silkfire Oct 15, 2025
4c57a4e
Remove requirement that a class must have at least one method annotat…
silkfire Oct 15, 2025
0ccab47
* Integer attribute values that fit within target type range should n…
silkfire Oct 16, 2025
485cc9b
Use a dummy syntax tree to test whether types are implicitly convertible
silkfire Oct 17, 2025
256f2aa
Move "Generic class must be abstract or annotated with a [GenericType…
silkfire Oct 17, 2025
58aee81
Add support to analyze implicit conversion from an array to a Span of…
silkfire Oct 17, 2025
748c7db
Add support to analyze implicit conversion when using constant values…
silkfire Oct 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ src/BenchmarkDotNet/Disassemblers/BenchmarkDotNet.Disassembler.*.nupkg
# Visual Studio 2015 cache/options directory
.vs/

# VSCode directory
.vscode/

# Cake
tools/**
.dotnet
Expand Down
43 changes: 43 additions & 0 deletions BenchmarkDotNet.Analyzers.sln
Original file line number Diff line number Diff line change
@@ -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
39 changes: 21 additions & 18 deletions NuGet.Config
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<clear />

<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
<!-- reuquired to run Mono AOT benchmarks -->
<add key="dotnet6" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" />
<add key="dotnet7" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json" />
<add key="dotnet8" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8/nuget/v3/index.json" />
<add key="dotnet9" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json" />
<add key="dotnet10" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10/nuget/v3/index.json" />
</packageSources>
</configuration>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<clear />

<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
<!-- required to run Mono AOT benchmarks -->
<add key="dotnet6" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" />
<add key="dotnet7" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json" />
<add key="dotnet8" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8/nuget/v3/index.json" />
<add key="dotnet9" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json" />
<add key="dotnet10" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10/nuget/v3/index.json" />

<!-- required for Roslyn analyzers -->
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
</packageSources>
</configuration>
73 changes: 73 additions & 0 deletions src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace BenchmarkDotNet.Analyzers
{
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Immutable;

internal static class AnalyzerHelper
{
public static LocalizableResourceString GetResourceString(string name) => new LocalizableResourceString(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<AttributeListSyntax> attributeLists, SemanticModel semanticModel) => AttributeListsContainAttribute(compilation.GetTypeByMetadataName(attributeName), attributeLists, semanticModel);

public static bool AttributeListsContainAttribute(INamedTypeSymbol attributeTypeSymbol, SyntaxList<AttributeListSyntax> attributeLists, SemanticModel semanticModel)
{
if (attributeTypeSymbol == null)
{
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 ImmutableArray<AttributeSyntax> GetAttributes(string attributeName, Compilation compilation, SyntaxList<AttributeListSyntax> attributeLists, SemanticModel semanticModel) => GetAttributes(compilation.GetTypeByMetadataName(attributeName), attributeLists, semanticModel);

public static ImmutableArray<AttributeSyntax> GetAttributes(INamedTypeSymbol attributeTypeSymbol, SyntaxList<AttributeListSyntax> attributeLists, SemanticModel semanticModel)
{
var attributesBuilder = ImmutableArray.CreateBuilder<AttributeSyntax>();

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();
}
}
}
2 changes: 2 additions & 0 deletions src/BenchmarkDotNet.Analyzers/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
; Shipped analyzer releases
; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
35 changes: 35 additions & 0 deletions src/BenchmarkDotNet.Analyzers/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
; Unshipped analyzer release
; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md

### New Rules

Rule ID | Category | Severity | Notes
---------|----------|----------|--------------------
BDN1000 | Usage | Error | BDN1000_BenchmarkRunner_Run_TypeArgumentClassMissingBenchmarkMethods
BDN1001 | Usage | Error | BDN1001_General_BenchmarkClass_MethodMustBePublic
BDN1002 | Usage | Error | BDN1002_General_BenchmarkClass_MethodMustBeNonGeneric
BDN1003 | Usage | Error | BDN1003_General_BenchmarkClass_ClassMustBePublic
BDN1004 | Usage | Error | BDN1004_General_BenchmarkClass_ClassMustBeNonStatic
BDN1005 | Usage | Error | BDN1005_General_BenchmarkClass_ClassMustBeNonAbstract
BDN1006 | Usage | Error | BDN1006_General_BenchmarkClass_ClassMustBeNonGeneric
BDN1007 | Usage | Error | BDN1007_General_BenchmarkClass_ClassWithGenericTypeArgumentsAttributeMustHaveTypeParameters
BDN1008 | Usage | Error | BDN1008_General_BenchmarkClass_GenericTypeArgumentsAttributeMustHaveMatchingTypeParameterCount
BDN1009 | Usage | Error | BDN1009_General_BenchmarkClass_ClassMustBeUnsealed
BDN1010 | Usage | Error | BDN1010_General_BenchmarkClass_OnlyOneMethodCanBeBaseline
BDN1011 | Usage | Error | BDN1011_Attributes_GeneralParameterAttributes_MutuallyExclusiveOnField
BDN1012 | Usage | Error | BDN1012_Attributes_GeneralParameterAttributes_MutuallyExclusiveOnProperty
BDN1013 | Usage | Error | BDN1013_Attributes_GeneralParameterAttributes_FieldMustBePublic
BDN1014 | Usage | Error | BDN1014_Attributes_GeneralParameterAttributes_PropertyMustBePublic
BDN1015 | Usage | Error | BDN1015_Attributes_GeneralParameterAttributes_NotValidOnReadonlyField
BDN1016 | Usage | Error | BDN1016_Attributes_GeneralParameterAttributes_NotValidOnConstantField
BDN1017 | Usage | Error | BDN1017_Attributes_GeneralParameterAttributes_PropertyCannotBeInitOnly
BDN1018 | Usage | Error | BDN1018_Attributes_GeneralParameterAttributes_PropertyMustHavePublicSetter
BDN1019 | Usage | Error | BDN1019_Attributes_ParamsAttribute_MustHaveValues
BDN1020 | Usage | Error | BDN1020_Attributes_ParamsAttribute_UnexpectedValueType
BDN1021 | Usage | Warning | BDN1021_Attributes_ParamsAttribute_UnnecessarySingleValuePassedToAttribute
BDN1022 | Usage | Error | BDN1022_Attributes_ParamsAllValuesAttribute_NotAllowedOnFlagsEnumPropertyOrFieldType
BDN1023 | Usage | Error | BDN1023_Attributes_ParamsAllValues_PropertyOrFieldTypeMustBeEnumOrBool
BDN1024 | Usage | Error | BDN1024_Attributes_ArgumentsAttribute_RequiresBenchmarkAttribute
BDN1025 | Usage | Error | BDN1025_Attributes_ArgumentsAttribute_MethodWithoutAttributeMustHaveNoParameters
BDN1026 | Usage | Error | BDN1026_Attributes_ArgumentsAttribute_MustHaveMatchingValueCount
BDN1027 | Usage | Error | BDN1027_Attributes_ArgumentsAttribute_MustHaveMatchingValueType
Loading