Skip to content

Commit 851b45e

Browse files
committed
Move "Generic class must be abstract or annotated with a [GenericTypeArgumentsAttribute]" to Run analyzer and remove abstract modifier requirement
1 parent 8cccd86 commit 851b45e

File tree

9 files changed

+442
-237
lines changed

9 files changed

+442
-237
lines changed

src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
using Microsoft.CodeAnalysis;
44
using Microsoft.CodeAnalysis.CSharp;
55
using Microsoft.CodeAnalysis.CSharp.Syntax;
6-
6+
using System.Collections.Generic;
77
using System.Collections.Immutable;
8+
using System.Linq;
89

910
internal static class AnalyzerHelper
1011
{
@@ -16,7 +17,7 @@ internal static class AnalyzerHelper
1617

1718
public static bool AttributeListsContainAttribute(INamedTypeSymbol? attributeTypeSymbol, SyntaxList<AttributeListSyntax> attributeLists, SemanticModel semanticModel)
1819
{
19-
if (attributeTypeSymbol == null)
20+
if (attributeTypeSymbol == null || attributeTypeSymbol.TypeKind == TypeKind.Error)
2021
{
2122
return false;
2223
}
@@ -41,6 +42,18 @@ public static bool AttributeListsContainAttribute(INamedTypeSymbol? attributeTyp
4142
return false;
4243
}
4344

45+
public static bool AttributeListContainsAttribute(string attributeName, Compilation compilation, ImmutableArray<AttributeData> attributeList) => AttributeListContainsAttribute(compilation.GetTypeByMetadataName(attributeName), attributeList);
46+
47+
public static bool AttributeListContainsAttribute(INamedTypeSymbol? attributeTypeSymbol, ImmutableArray<AttributeData> attributeList)
48+
{
49+
if (attributeTypeSymbol == null || attributeTypeSymbol.TypeKind == TypeKind.Error)
50+
{
51+
return false;
52+
}
53+
54+
return attributeList.Any(ad => ad.AttributeClass != null && ad.AttributeClass.Equals(attributeTypeSymbol, SymbolEqualityComparer.Default));
55+
}
56+
4457
public static ImmutableArray<AttributeSyntax> GetAttributes(string attributeName, Compilation compilation, SyntaxList<AttributeListSyntax> attributeLists, SemanticModel semanticModel) => GetAttributes(compilation.GetTypeByMetadataName(attributeName), attributeLists, semanticModel);
4558

4659
public static ImmutableArray<AttributeSyntax> GetAttributes(INamedTypeSymbol? attributeTypeSymbol, SyntaxList<AttributeListSyntax> attributeLists, SemanticModel semanticModel)

src/BenchmarkDotNet.Analyzers/AnalyzerReleases.Unshipped.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ Rule ID | Category | Severity | Notes
77
---------|----------|----------|--------------------
88
BDN1000 | Usage | Error | BDN1000_BenchmarkRunner_Run_TypeArgumentClassMissingBenchmarkMethods
99
BDN1001 | Usage | Error | BDN1001_BenchmarkRunner_Run_TypeArgumentClassMustBePublic
10-
BDN1002 | Usage | Error | BDN1002_BenchmarkRunner_Run_TypeArgumentClassMustBeNonAbstract
11-
BDN1003 | Usage | Error | BDN1003_BenchmarkRunner_Run_TypeArgumentClassMustBeUnsealed
12-
BDN1100 | Usage | Error | BDN1100_General_BenchmarkClass_MethodMustBePublic
13-
BDN1101 | Usage | Error | BDN1101_General_BenchmarkClass_MethodMustBeNonGeneric
14-
BDN1102 | Usage | Error | BDN1102_General_BenchmarkClass_ClassMustBeNonStatic
15-
BDN1103 | Usage | Error | BDN1103_General_BenchmarkClass_ClassMustBeNonAbstract
16-
BDN1104 | Usage | Error | BDN1104_General_BenchmarkClass_ClassMustBeNonGeneric
17-
BDN1105 | Usage | Error | BDN1105_General_BenchmarkClass_ClassWithGenericTypeArgumentsAttributeMustBeGeneric
18-
BDN1106 | Usage | Error | BDN1106_General_BenchmarkClass_GenericTypeArgumentsAttributeMustHaveMatchingTypeParameterCount
19-
BDN1107 | Usage | Error | BDN1107_General_BenchmarkClass_OnlyOneMethodCanBeBaseline
10+
BDN1002 | Usage | Error | BDN1002_BenchmarkRunner_Run_TypeArgumentClassMustBeUnsealed
11+
BDN1003 | Usage | Error | BDN1003_BenchmarkRunner_Run_TypeArgumentClassMustBeNonAbstract
12+
BDN1004 | Usage | Error | BDN1004_BenchmarkRunner_Run_GenericTypeArgumentClassMustBeAnnotatedWithAGenericTypeArgumentsAttribute
13+
BDN1100 | Usage | Error | BDN1100_General_BenchmarkClass_ClassWithGenericTypeArgumentsAttributeMustBeNonAbstract
14+
BDN1101 | Usage | Error | BDN1101_General_BenchmarkClass_ClassWithGenericTypeArgumentsAttributeMustBeGeneric
15+
BDN1102 | Usage | Error | BDN1102_General_BenchmarkClass_GenericTypeArgumentsAttributeMustHaveMatchingTypeParameterCount
16+
BDN1103 | Usage | Error | BDN1103_General_BenchmarkClass_MethodMustBePublic
17+
BDN1104 | Usage | Error | BDN1104_General_BenchmarkClass_MethodMustBeNonGeneric
18+
BDN1105 | Usage | Error | BDN1105_General_BenchmarkClass_ClassMustBeNonStatic
19+
BDN1106 | Usage | Error | BDN1106_General_BenchmarkClass_OnlyOneMethodCanBeBaseline
2020
BDN1200 | Usage | Error | BDN1200_Attributes_GeneralParameterAttributes_MutuallyExclusiveOnField
2121
BDN1201 | Usage | Error | BDN1201_Attributes_GeneralParameterAttributes_MutuallyExclusiveOnProperty
2222
BDN1202 | Usage | Error | BDN1202_Attributes_GeneralParameterAttributes_FieldMustBePublic

src/BenchmarkDotNet.Analyzers/BenchmarkDotNetAnalyzerResources.Designer.cs

Lines changed: 30 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/BenchmarkDotNet.Analyzers/BenchmarkDotNetAnalyzerResources.resx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,20 @@
144144
<data name="General_BenchmarkClass_ClassWithGenericTypeArgumentsAttributeMustBeNonAbstract_MessageFormat" xml:space="preserve">
145145
<value>Benchmark class '{0}' cannot be abstract</value>
146146
</data>
147-
<data name="General_BenchmarkClass_GenericClassMustBeAbstractOrAnnotatedWithAGenericTypeArgumentsAttribute_MessageFormat" xml:space="preserve">
148-
<value>Benchmark class '{0}' cannot be generic unless declared as abstract or annotated with a [GenericTypeArguments] attribute</value>
147+
<data name="BenchmarkRunner_Run_GenericTypeArgumentClassMustBeAnnotatedWithAGenericTypeArgumentsAttribute_MessageFormat" xml:space="preserve">
148+
<value>Referenced generic benchmark class '{0}' has no [GenericTypeArguments] attribute(s)</value>
149+
</data>
150+
<data name="BenchmarkRunner_Run_GenericTypeArgumentClassMustBeAnnotatedWithAGenericTypeArgumentsAttribute_Description" xml:space="preserve">
151+
<value>A generic benchmark class referenced in the BenchmarkRunner.Run method must be must be annotated with at least one [GenericTypeArguments] attribute</value>
149152
</data>
150153
<data name="General_BenchmarkClass_ClassMustBeNonStatic_Title" xml:space="preserve">
151154
<value>Benchmark classes must be non-static</value>
152155
</data>
153156
<data name="General_BenchmarkClass_ClassWithGenericTypeArgumentsAttributeMustBeNonAbstract_Title" xml:space="preserve">
154157
<value>Benchmark classes annotated with the [GenericTypeArguments] attribute must be non-abstract</value>
155158
</data>
156-
<data name="General_BenchmarkClass_GenericClassMustBeAbstractOrAnnotatedWithAGenericTypeArgumentsAttribute_Title" xml:space="preserve">
157-
<value>Benchmark classes can only be generic if they're either abstract or annotated with a [GenericTypeArguments] attribute</value>
159+
<data name="BenchmarkRunner_Run_GenericTypeArgumentClassMustBeAnnotatedWithAGenericTypeArgumentsAttribute_Title" xml:space="preserve">
160+
<value>Generic benchmark classes must be annotated with at least one [GenericTypeArguments] attribute</value>
158161
</data>
159162
<data name="BenchmarkRunner_Run_TypeArgumentClassMustBePublic_Title" xml:space="preserve">
160163
<value>Benchmark classes must be public</value>

src/BenchmarkDotNet.Analyzers/BenchmarkRunner/RunAnalyzer.cs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.CodeAnalysis.Diagnostics;
77

88
using System.Collections.Immutable;
9+
using System.Linq;
910

1011
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1112
public class RunAnalyzer : DiagnosticAnalyzer
@@ -25,6 +26,14 @@ public class RunAnalyzer : DiagnosticAnalyzer
2526
DiagnosticSeverity.Error,
2627
isEnabledByDefault: true);
2728

29+
internal static readonly DiagnosticDescriptor TypeArgumentClassMustBeUnsealedRule = new DiagnosticDescriptor(DiagnosticIds.BenchmarkRunner_Run_TypeArgumentClassMustBeUnsealed,
30+
AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_TypeArgumentClassMustBeUnsealed_Title)),
31+
AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_TypeArgumentClassMustBeUnsealed_MessageFormat)),
32+
"Usage",
33+
DiagnosticSeverity.Error,
34+
isEnabledByDefault: true,
35+
description: AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_TypeArgumentClassMustBeUnsealed_Description)));
36+
2837
internal static readonly DiagnosticDescriptor TypeArgumentClassMustBeNonAbstractRule = new DiagnosticDescriptor(DiagnosticIds.BenchmarkRunner_Run_TypeArgumentClassMustBeNonAbstract,
2938
AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_TypeArgumentClassMustBeNonAbstract_Title)),
3039
AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_TypeArgumentClassMustBeNonAbstract_MessageFormat)),
@@ -33,20 +42,22 @@ public class RunAnalyzer : DiagnosticAnalyzer
3342
isEnabledByDefault: true,
3443
description: AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_TypeArgumentClassMustBeNonAbstract_Description)));
3544

36-
internal static readonly DiagnosticDescriptor TypeArgumentClassMustBeUnsealedRule = new DiagnosticDescriptor(DiagnosticIds.BenchmarkRunner_Run_TypeArgumentClassMustBeUnsealed,
37-
AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_TypeArgumentClassMustBeUnsealed_Title)),
38-
AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_TypeArgumentClassMustBeUnsealed_MessageFormat)),
39-
"Usage",
40-
DiagnosticSeverity.Error,
41-
isEnabledByDefault: true,
42-
description: AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_TypeArgumentClassMustBeUnsealed_Description)));
45+
internal static readonly DiagnosticDescriptor GenericTypeArgumentClassMustBeAnnotatedWithAGenericTypeArgumentsAttributeRule = new DiagnosticDescriptor(DiagnosticIds.BenchmarkRunner_Run_GenericTypeArgumentClassMustBeAnnotatedWithAGenericTypeArgumentsAttribute,
46+
AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_GenericTypeArgumentClassMustBeAnnotatedWithAGenericTypeArgumentsAttribute_Title)),
47+
AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_GenericTypeArgumentClassMustBeAnnotatedWithAGenericTypeArgumentsAttribute_MessageFormat)),
48+
"Usage",
49+
DiagnosticSeverity.Error,
50+
isEnabledByDefault: true,
51+
description: AnalyzerHelper.GetResourceString(nameof(BenchmarkDotNetAnalyzerResources.BenchmarkRunner_Run_GenericTypeArgumentClassMustBeAnnotatedWithAGenericTypeArgumentsAttribute_Description)));
52+
4353

4454
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
4555
[
4656
TypeArgumentClassMissingBenchmarkMethodsRule,
4757
TypeArgumentClassMustBePublicRule,
58+
TypeArgumentClassMustBeUnsealedRule,
4859
TypeArgumentClassMustBeNonAbstractRule,
49-
TypeArgumentClassMustBeUnsealedRule
60+
GenericTypeArgumentClassMustBeAnnotatedWithAGenericTypeArgumentsAttributeRule,
5061
];
5162

5263
public override void Initialize(AnalysisContext analysisContext)
@@ -143,8 +154,6 @@ private static void Analyze(SyntaxNodeAnalysisContext context)
143154
var benchmarkAttributeTypeSymbol = AnalyzerHelper.GetBenchmarkAttributeTypeSymbol(context.Compilation);
144155
if (benchmarkAttributeTypeSymbol == null)
145156
{
146-
ReportDiagnostic(TypeArgumentClassMissingBenchmarkMethodsRule);
147-
148157
return;
149158
}
150159

@@ -168,6 +177,11 @@ private static void Analyze(SyntaxNodeAnalysisContext context)
168177
ReportDiagnostic(TypeArgumentClassMustBeUnsealedRule);
169178
}
170179

180+
if (benchmarkClassTypeSymbol.IsUnboundGenericType && !AnalyzerHelper.AttributeListContainsAttribute("BenchmarkDotNet.Attributes.GenericTypeArgumentsAttribute", context.Compilation, benchmarkClassTypeSymbol.GetAttributes()))
181+
{
182+
ReportDiagnostic(GenericTypeArgumentClassMustBeAnnotatedWithAGenericTypeArgumentsAttributeRule);
183+
}
184+
171185
return;
172186

173187
bool HasBenchmarkAttribute()

0 commit comments

Comments
 (0)