diff --git a/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs b/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs
index df56308025..7acfc97788 100644
--- a/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs
+++ b/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs
@@ -126,7 +126,7 @@ public void BuildAnalyzers()
context.Information("BuildSystemProvider: " + context.BuildSystem().Provider);
// Each band must restore as well as build: the PackageReference is chosen by MccVersion, so reusing the
// default band's project.assets.json silently compiles every band against the default's Roslyn.
- string[] mccVersions = ["2.8", "3.0", "3.8", "4.4", "4.8", "4.12"];
+ string[] mccVersions = ["4.0", "4.4", "4.8", "4.12"];
foreach (string version in mccVersions)
{
context.DotNetBuild(context.AnalyzersProjectFile.FullPath, new DotNetBuildSettings
diff --git a/build/roslynBands.props b/build/roslynBands.props
index 07375d64ef..a207557fb2 100644
--- a/build/roslynBands.props
+++ b/build/roslynBands.props
@@ -10,11 +10,10 @@
bin\$(Configuration)\roslyn$(MccVersion)\cs
false
-
-
- $(DefineConstants);CODE_ANALYSIS_3_0
-
- $(DefineConstants);CODE_ANALYSIS_3_8
+
$(DefineConstants);CODE_ANALYSIS_4_4
diff --git a/src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs b/src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs
index 3a5450691c..6db6b69a5b 100644
--- a/src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs
+++ b/src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs
@@ -148,23 +148,14 @@ public static bool MayBeRefLike(ITypeSymbol type)
return true;
}
#endif
- return IsRefLikeType(type);
+ return type.IsRefLikeType;
}
- // ref structs are C# 7.2, but no public symbol carries IsRefLikeType until Roslyn 3.0; the oldest band reads
- // the internal property behind it instead.
- private static bool IsRefLikeType(ITypeSymbol type)
-#if CODE_ANALYSIS_3_0
- => type.IsRefLikeType;
-#else
- => RefLikeTypePolyfill.IsRefLikeType(type);
-#endif
-
///
/// Names which of the two found, so a message reads the same from either source analyzer.
///
public static string ByRefLikeClause(ITypeSymbol type)
- => IsRefLikeType(type) ? "is a ref struct" : "admits a ref struct";
+ => type.IsRefLikeType ? "is a ref struct" : "admits a ref struct";
///
/// Which of the two rules applies. A ref struct is one the compiler can see, so the source cannot work; a
@@ -172,7 +163,7 @@ public static string ByRefLikeClause(ITypeSymbol type)
/// perfectly well - so the two are separate ids, configurable apart.
///
public static DiagnosticDescriptor ByRefLikeRule(ITypeSymbol type)
- => IsRefLikeType(type) ? SourceElementMustNotBeByRefLikeRule : SourceElementMayBeByRefLikeRule;
+ => type.IsRefLikeType ? SourceElementMustNotBeByRefLikeRule : SourceElementMayBeByRefLikeRule;
///
/// Whether is or derives from it. BenchmarkDotNet resolves
diff --git a/src/BenchmarkDotNet.Analyzers/Attributes/GeneralParameterAttributesAnalyzer.cs b/src/BenchmarkDotNet.Analyzers/Attributes/GeneralParameterAttributesAnalyzer.cs
index 5c67e8c25a..f661af121a 100644
--- a/src/BenchmarkDotNet.Analyzers/Attributes/GeneralParameterAttributesAnalyzer.cs
+++ b/src/BenchmarkDotNet.Analyzers/Attributes/GeneralParameterAttributesAnalyzer.cs
@@ -197,10 +197,7 @@ private static void Analyze(SyntaxNodeAnalysisContext context)
// `init` counts: the runnable assigns parameters through an object initializer.
var propertyAccessors = propertyDeclarationSyntax.AccessorList?.Accessors;
propertyIsMissingAssignableSetter = !HasAssignableAccessor(propertyAccessors, SyntaxKind.SetAccessorDeclaration)
-#if CODE_ANALYSIS_3_8
- && !HasAssignableAccessor(propertyAccessors, SyntaxKind.InitAccessorDeclaration)
-#endif
- ;
+ && !HasAssignableAccessor(propertyAccessors, SyntaxKind.InitAccessorDeclaration);
fieldOrPropertyIdentifierLocation = propertyDeclarationSyntax.Identifier.GetLocation();
declaredNames = ImmutableArray.Create((fieldOrPropertyIdentifier, fieldOrPropertyIdentifierLocation));
diff --git a/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.csproj b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.csproj
index 722341534f..ba0987eb7d 100644
--- a/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.csproj
+++ b/src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.csproj
@@ -8,11 +8,12 @@
$(NoWarn);RS2007
-
- $(NoWarn);RS2002
-
-
-
+
+ $(NoWarn);RS1024
+
+
+
diff --git a/src/BenchmarkDotNet.Analyzers/Polyfills/RefLikeTypePolyfill.cs b/src/BenchmarkDotNet.Analyzers/Polyfills/RefLikeTypePolyfill.cs
deleted file mode 100644
index 21f1911e42..0000000000
--- a/src/BenchmarkDotNet.Analyzers/Polyfills/RefLikeTypePolyfill.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-#if !CODE_ANALYSIS_3_0
-using System.Reflection;
-using Microsoft.CodeAnalysis.CSharp;
-
-namespace Microsoft.CodeAnalysis;
-
-// ITypeSymbol.IsRefLikeType only appears in Roslyn 3.0 (dotnet/roslyn#30426), though the compiler has tracked
-// ref-likeness since C# 7.2 - as the internal TypeSymbol.IsByRefLikeType this reads. Reflection rather than the
-// metadata attribute because that route is deliberately closed: PENamedTypeSymbol.GetAttributes filters
-// IsByRefLikeAttribute out exactly when the type is ref-like, so a referenced Span reports no such attribute.
-// The property is declared on the base and overridden per symbol kind, so one lookup serves every kind.
-//
-// Both guards below answer false rather than throwing, because an exception escaping an analyzer is reported as
-// AD0001 and disables it. The lookup is guarded because a failure in a static initializer poisons the type for
-// every later call; the instance check is guarded because GetValue is the one call here that can throw, and does
-// so only for an ITypeSymbol that is not a Roslyn C# symbol - which the language filter on every analyzer here
-// makes unreachable, RS1009 forbids implementing, and a runtime mock could still produce.
-internal static class RefLikeTypePolyfill
-{
- private static readonly PropertyInfo? IsByRefLikeType = ResolveIsByRefLikeType();
-
- internal static bool IsRefLikeType(ITypeSymbol type)
- => IsByRefLikeType is { } property
- && property.DeclaringType!.IsInstanceOfType(type)
- && property.GetValue(type) is true;
-
- private static PropertyInfo? ResolveIsByRefLikeType()
- {
- try
- {
- return typeof(CSharpCompilation).Assembly
- .GetType("Microsoft.CodeAnalysis.CSharp.Symbols.TypeSymbol")
- ?.GetProperty("IsByRefLikeType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
- }
- catch
- {
- return null;
- }
- }
-}
-#endif
diff --git a/src/BenchmarkDotNet.Analyzers/Polyfills/SymbolEqualityComparer.cs b/src/BenchmarkDotNet.Analyzers/Polyfills/SymbolEqualityComparer.cs
deleted file mode 100644
index ac350dc5fb..0000000000
--- a/src/BenchmarkDotNet.Analyzers/Polyfills/SymbolEqualityComparer.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-#if !CODE_ANALYSIS_3_8
-using System.Collections.Generic;
-
-namespace Microsoft.CodeAnalysis;
-
-// SymbolEqualityComparer appears in Roslyn 3.3, so the 2.8 and 3.0 bands compare symbols through ISymbol.Equals,
-// as callers did before it existed. Declared in Roslyn's own namespace, so the call sites read the same on
-// every band.
-internal sealed class SymbolEqualityComparer : IEqualityComparer
-{
- internal static readonly SymbolEqualityComparer Default = new();
-
- private SymbolEqualityComparer() { }
-
- public bool Equals(ISymbol? x, ISymbol? y) => x is null ? y is null : x.Equals(y);
-
- public int GetHashCode(ISymbol obj) => obj.GetHashCode();
-}
-#endif
diff --git a/src/BenchmarkDotNet.Annotations/buildTransitive/netstandard2.0/BenchmarkDotNet.Annotations.targets b/src/BenchmarkDotNet.Annotations/buildTransitive/netstandard2.0/BenchmarkDotNet.Annotations.targets
index c6dda74663..16e87db341 100644
--- a/src/BenchmarkDotNet.Annotations/buildTransitive/netstandard2.0/BenchmarkDotNet.Annotations.targets
+++ b/src/BenchmarkDotNet.Annotations/buildTransitive/netstandard2.0/BenchmarkDotNet.Annotations.targets
@@ -8,4 +8,24 @@
+
+
+
+ <_BenchmarkDotNetAnalyzer Include="@(Analyzer)" Condition="'%(Analyzer.NuGetPackageId)' == 'BenchmarkDotNet.Annotations'" />
+
+
+
+
+
+
+
+
+