From 1010f9719090ccca450c7d1ad0923cdbd687b047 Mon Sep 17 00:00:00 2001 From: Gabriel Bider <1554615+silkfire@users.noreply.github.com> Date: Sat, 15 Nov 2025 01:47:32 +0100 Subject: [PATCH 1/3] Fix issue #2864 --- .../AnalyzerHelper.cs | 47 ++- .../Attributes/ArgumentsAttributeAnalyzer.cs | 104 ++++- .../Attributes/ParamsAttributeAnalyzer.cs | 93 ++++- .../BenchmarkDotNet.Analyzers.csproj | 2 +- .../ArgumentsAttributeAnalyzerTests.cs | 363 ++++++++++++++++-- .../ParamsAttributeAnalyzerTests.cs | 324 +++++++++++++++- .../BenchmarkDotNet.Analyzers.Tests.csproj | 6 +- .../Fixtures/AnalyzerTestFixture.cs | 29 +- 8 files changed, 886 insertions(+), 82 deletions(-) diff --git a/src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs b/src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs index d23ca9db89..ab65c0219b 100644 --- a/src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs +++ b/src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs @@ -10,6 +10,8 @@ namespace BenchmarkDotNet.Analyzers; internal static class AnalyzerHelper { + internal const string InterceptorsNamespaces = "InterceptorsNamespaces"; + public static LocalizableResourceString GetResourceString(string name) => new(name, BenchmarkDotNetAnalyzerResources.ResourceManager, typeof(BenchmarkDotNetAnalyzerResources)); @@ -143,48 +145,58 @@ public static string NormalizeTypeName(INamedTypeSymbol namedTypeSymbol) return typeName; } - public static bool IsAssignableToField(Compilation compilation, ITypeSymbol targetType, string valueExpression, Optional constantValue, string? valueType) + public static bool IsAssignableToField(Compilation compilation, LanguageVersion languageVersion, string? valueTypeContainingNamespace, ITypeSymbol targetType, string valueExpression, Optional constantValue, string? valueType) { const string codeTemplate1 = """ + {0} + file static class Internal {{ - static readonly {0} x = {1}; + static readonly {1} x = {2}; }} """; const string codeTemplate2 = """ + {0} + file static class Internal {{ - static readonly {0} x = ({1}){2}; + static readonly {1} x = ({2}){3}; }} """; - return IsAssignableTo(codeTemplate1, codeTemplate2, compilation, targetType, valueExpression, constantValue, valueType); + return IsAssignableTo(codeTemplate1, codeTemplate2, compilation, languageVersion, valueTypeContainingNamespace, targetType, valueExpression, constantValue, valueType); } - public static bool IsAssignableToLocal(Compilation compilation, ITypeSymbol targetType, string valueExpression, Optional constantValue, string? valueType) + public static bool IsAssignableToLocal(Compilation compilation, LanguageVersion languageVersion, string? valueTypeContainingNamespace, ITypeSymbol targetType, string valueExpression, Optional constantValue, string? valueType) { const string codeTemplate1 = """ + {0} + file static class Internal {{ static void Method() {{ - {0} x = {1}; + {1} x = {2}; }} }} """; const string codeTemplate2 = """ + {0} + file static class Internal {{ static void Method() {{ - {0} x = ({1}){2}; + {1} x = ({2}){3}; }} }} """; - return IsAssignableTo(codeTemplate1, codeTemplate2, compilation, targetType, valueExpression, constantValue, valueType); + return IsAssignableTo(codeTemplate1, codeTemplate2, compilation, languageVersion, valueTypeContainingNamespace, targetType, valueExpression, constantValue, valueType); } - private static bool IsAssignableTo(string codeTemplate1, string codeTemplate2, Compilation compilation, ITypeSymbol targetType, string valueExpression, Optional constantValue, string? valueType) + private static bool IsAssignableTo(string codeTemplate1, string codeTemplate2, Compilation compilation, LanguageVersion languageVersion, string? valueTypeContainingNamespace, ITypeSymbol targetType, string valueExpression, Optional constantValue, string? valueType) { - var hasCompilerDiagnostics = HasNoCompilerDiagnostics(string.Format(codeTemplate1, targetType, valueExpression), compilation); - if (hasCompilerDiagnostics) + var usingDirective = valueTypeContainingNamespace != null ? $"using {valueTypeContainingNamespace};" : ""; + + var hasNoCompilerDiagnostics = HasNoCompilerDiagnostics(string.Format(codeTemplate1, usingDirective, targetType, valueExpression), compilation, languageVersion); + if (hasNoCompilerDiagnostics) { return true; } @@ -200,16 +212,19 @@ private static bool IsAssignableTo(string codeTemplate1, string codeTemplate2, C return false; } - return HasNoCompilerDiagnostics(string.Format(codeTemplate2, targetType, valueType, constantLiteral), compilation); + return HasNoCompilerDiagnostics(string.Format(codeTemplate2, usingDirective, targetType, valueType, constantLiteral), compilation, languageVersion); } - private static bool HasNoCompilerDiagnostics(string code, Compilation compilation) + private static bool HasNoCompilerDiagnostics(string code, Compilation compilation, LanguageVersion languageVersion) { - var syntaxTree = CSharpSyntaxTree.ParseText(code); + var compilationTestSyntaxTree = CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(languageVersion)); + + var syntaxTreesWithInterceptorsNamespaces = compilation.SyntaxTrees.Where(st => st.Options.Features.ContainsKey(InterceptorsNamespaces)); var compilerDiagnostics = compilation - .AddSyntaxTrees(syntaxTree) - .GetSemanticModel(syntaxTree) + .RemoveSyntaxTrees(syntaxTreesWithInterceptorsNamespaces) + .AddSyntaxTrees(compilationTestSyntaxTree) + .GetSemanticModel(compilationTestSyntaxTree) .GetMethodBodyDiagnostics() .Where(d => d.DefaultSeverity == DiagnosticSeverity.Error) .ToList(); diff --git a/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs b/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs index 6b6540aea5..a1c23254cc 100644 --- a/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs +++ b/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs @@ -281,30 +281,110 @@ void ReportIfNotImplicitlyConvertibleValueTypeDiagnostic(Func$(NoWarn);CS1591 - + diff --git a/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs b/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs index c9a554a326..3b6dffe9d5 100644 --- a/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs +++ b/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs @@ -1,5 +1,6 @@ using BenchmarkDotNet.Analyzers.Attributes; using BenchmarkDotNet.Analyzers.Tests.Fixtures; +using Microsoft.CodeAnalysis.CSharp; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -401,6 +402,60 @@ public void BenchmarkMethod() await RunAsync(); } + [Theory, CombinatorialData] + public async Task Analyzing_an_arguments_attribute_should_not_throw_an_inconsistent_language_versions_exception( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) + { + var testCode = /* lang=c#-test */ $$""" + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "42")}}] + public void BenchmarkMethod(int a) + { + + } + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + ReferenceDummyEnum(); + SetParseOptions(LanguageVersion.CSharp14); + + await RunAsync(); + } + + [Theory, CombinatorialData] + public async Task Analyzing_an_arguments_attribute_should_not_throw_an_inconsistent_syntax_tree_features_exception( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) + { + var testCode = /* lang=c#-test */ $$""" + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "42")}}] + public void BenchmarkMethod(int a) + { + + } + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + ReferenceDummyEnum(); + SetParseOptions(LanguageVersion.Default, true); + + 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) @@ -449,6 +504,60 @@ public void BenchmarkMethod({{parameters}}) await RunAsync(); } + [Theory, CombinatorialData] + public async Task Providing_an_unknown_value_type_should_not_trigger_diagnostic( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) + { + var testCode = /* lang=c#-test */ $$""" + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "dummy_literal, true")}}] + public void BenchmarkMethod(byte a, bool b) + { + + } + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + + DisableCompilerDiagnostics(); + + await RunAsync(); + } + + [Theory, CombinatorialData] + public async Task Providing_an_unknown_type_in_typeof_expression_should_not_trigger_diagnostic( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) + { + var testCode = /* lang=c#-test */ $$""" + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "typeof(int), typeof(dummy_literal)")}}] + public void BenchmarkMethod(System.Type a, System.Type b) + { + + } + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + + DisableCompilerDiagnostics(); + + await RunAsync(); + } + [Theory, CombinatorialData] public async Task Providing_expected_value_type_should_not_trigger_diagnostic( [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, @@ -456,6 +565,8 @@ public async Task Providing_expected_value_type_should_not_trigger_diagnostic( [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) { var testCode = /* lang=c#-test */ $$""" + using DifferentNamespace; + using BenchmarkDotNet.Attributes; public class BenchmarkClass @@ -472,6 +583,185 @@ public void BenchmarkMethod({{valueAndType.Value2}} a) TestCode = testCode; ReferenceDummyAttribute(); ReferenceDummyEnum(); + ReferenceDummyEnumInDifferentNamespace(); + + await RunAsync(); + } + + [Theory, CombinatorialData] + public async Task Providing_expected_enum_value_type_using_not_fully_qualified_name_located_in_a_different_namespace_should_not_trigger_diagnostic( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument, + bool isNullable) + { + var testCode = /* lang=c#-test */ $$""" + using DifferentNamespace; + + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1")}}] + public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) + { + + } + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + ReferenceDummyEnum(); + ReferenceDummyEnumInDifferentNamespace(); + + await RunAsync(); + } + + [Theory, CombinatorialData] + public async Task Providing_expected_enum_value_type_using_not_fully_qualified_name_located_in_same_namespace_should_not_trigger_diagnostic( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument, + bool isNullable) + { + var testCode = /* lang=c#-test */ $$""" + using BenchmarkDotNet.Attributes; + + namespace DifferentNamespace; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1")}}] + public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) + { + + } + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + ReferenceDummyEnum(); + ReferenceDummyEnumInDifferentNamespace(); + + await RunAsync(); + } + + [Theory, CombinatorialData] + public async Task Providing_expected_enum_value_type_array_using_not_fully_qualified_name_located_in_a_different_namespace_should_not_trigger_diagnostic( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ArrayValuesContainerAttributeArgumentEnumerableLocal))] string arrayValuesContainerAttributeArgument, + bool isNullable) + { + var testCode = /* lang=c#-test */ $$""" + using DifferentNamespace; + + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [{{dummyAttributeUsage}}{{string.Format(arrayValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1", $"DummyEnumInDifferentNamespace{(isNullable ? "?" : "")}")}}] + public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) + { + + } + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + ReferenceDummyEnum(); + ReferenceDummyEnumInDifferentNamespace(); + DisableCompilerDiagnostics(); // Nullable struct arrays are not supported in attributes + + await RunAsync(); + } + + [Theory, CombinatorialData] + public async Task Providing_expected_enum_value_type_array_using_not_fully_qualified_name_located_in_same_namespace_should_not_trigger_diagnostic( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ArrayValuesContainerAttributeArgumentEnumerableLocal))] string arrayValuesContainerAttributeArgument, + bool isNullable) + { + var testCode = /* lang=c#-test */ $$""" + using BenchmarkDotNet.Attributes; + + namespace DifferentNamespace; + + public class BenchmarkClass + { + [{{dummyAttributeUsage}}{{string.Format(arrayValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1", $"DummyEnumInDifferentNamespace{(isNullable ? "?" : "")}")}}] + public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) + { + + } + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + ReferenceDummyEnum(); + ReferenceDummyEnumInDifferentNamespace(); + DisableCompilerDiagnostics(); // Nullable struct arrays are not supported in attributes + + await RunAsync(); + } + + [Theory, CombinatorialData] + public async Task Providing_expected_type_using_not_fully_qualified_name_located_in_same_namespace_should_not_trigger_diagnostic( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ValuesAndTypesInDifferentNamespace))] ValueTupleDouble valueAndType, + [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) + { + var testCode = /* lang=c#-test */ $$""" + using BenchmarkDotNet.Attributes; + + namespace DifferentNamespace; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, valueAndType.Value1)}}] + public void BenchmarkMethod({{valueAndType.Value2}} a) + { + + } + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + ReferenceDummyEnumInDifferentNamespace(); + + await RunAsync(); + } + + [Theory, CombinatorialData] + public async Task Providing_expected_type_using_not_fully_qualified_name_located_in_a_different_namespace_should_not_trigger_diagnostic( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ValuesAndTypesInDifferentNamespace))] ValueTupleDouble valueAndType, + [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) + { + var testCode = /* lang=c#-test */ $$""" + using DifferentNamespace; + + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, valueAndType.Value1)}}] + public void BenchmarkMethod({{valueAndType.Value2}} a) + { + + } + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + ReferenceDummyEnumInDifferentNamespace(); await RunAsync(); } @@ -678,33 +968,6 @@ public void BenchmarkMethod(unkown a, string b) await RunAsync(); } - [Theory, CombinatorialData] - public async Task Providing_an_unkown_value_type_should_not_trigger_diagnostic( - [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, - [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) - { - var testCode = /* lang=c#-test */ $$""" - using BenchmarkDotNet.Attributes; - - public class BenchmarkClass - { - [Benchmark] - [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "{|#0:dummy_literal|}, true")}}] - public void BenchmarkMethod(byte a, bool b) - { - - } - } - """; - - TestCode = testCode; - ReferenceDummyAttribute(); - - DisableCompilerDiagnostics(); - - await RunAsync(); - } - [Theory, CombinatorialData] public async Task Providing_an_unexpected_or_not_implicitly_convertible_value_type_should_trigger_diagnostic( [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, @@ -981,6 +1244,44 @@ public static IEnumerable ScalarValuesContainerAttributeArgumentEnumerab ("-9223372036854775808", "ulong", "long"), ]; + public static IEnumerable ArrayValuesContainerAttributeArgumentEnumerableLocal() + { + string[] nameColonUsages = + [ + "", + "values: " + ]; + + string[] priorityNamedParameterUsages = + [ + "", + ", Priority = 1" + ]; + + List arrayValuesContainers = + [ + "{0}new object[] {{{{ new[] {{{{ {{0}} }}}} }}}}{1}", // new object[] { new[] { 42 } } + "{0}new object[] {{{{ new {{1}}[] {{{{ {{0}} }}}} }}}}{1}", // new object[] { new int[] { 42 } } + "{0}[ new[] {{{{ {{0}} }}}} ]{1}", // [ new[] { 42 } ] + "{0}[ new {{1}}[] {{{{ {{0}} }}}} ]{1}", // [ new int[] { 42 } ] + "{0}new object[] {{{{ new {{1}}[] {{{{ }}}} }}}}{1}", // new object[] { new int[] { } } + "{0}[ new {{1}}[] {{{{ }}}} ]{1}", // [ new int[] { } ] + "{0}new object[] {{{{ new {{1}}[0] }}}}{1}", // new object[] { new int[0] } + "{0}[ new {{1}}[0] ]{1}" // [ new int[0] ] + ]; + + foreach (var arrayValuesContainer in arrayValuesContainers) + { + foreach (var nameColonUsage in nameColonUsages) + { + foreach (var priorityNamedParameterUsage in priorityNamedParameterUsages) + { + yield return string.Format(arrayValuesContainer, nameColonUsage, priorityNamedParameterUsage); + } + } + } + } + public static IEnumerable> ValuesAndTypes => [ ( "true", "bool" ), @@ -1003,11 +1304,19 @@ public static IEnumerable ScalarValuesContainerAttributeArgumentEnumerab (object)"test_object" """, "object" ), ( "typeof(string)", "System.Type" ), + ( "typeof(DummyEnumInDifferentNamespace?)", "System.Type" ), ( "DummyEnum.Value1", "DummyEnum" ), ( "new[] { 0, 1, 2 }", "int[]" ), ]; + public static IEnumerable> ValuesAndTypesInDifferentNamespace => + [ + ( "typeof(DummyEnumInDifferentNamespace)", "System.Type" ), + ( "typeof(DummyEnumInDifferentNamespace?)", "System.Type" ), + ( "DummyEnumInDifferentNamespace.Value1", "DummyEnumInDifferentNamespace" ), + ]; + public static IEnumerable> NotConvertibleValuesAndTypes => [ ( "true", "bool" ), diff --git a/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ParamsAttributeAnalyzerTests.cs b/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ParamsAttributeAnalyzerTests.cs index d4f2eeabb1..2157314993 100644 --- a/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ParamsAttributeAnalyzerTests.cs +++ b/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ParamsAttributeAnalyzerTests.cs @@ -1,5 +1,6 @@ using BenchmarkDotNet.Analyzers.Attributes; using BenchmarkDotNet.Analyzers.Tests.Fixtures; +using Microsoft.CodeAnalysis.CSharp; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -7,6 +8,7 @@ using Xunit; namespace BenchmarkDotNet.Analyzers.Tests.AnalyzerTests.Attributes; + public class ParamsAttributeAnalyzerTests { public class General : AnalyzerTestFixture @@ -175,6 +177,52 @@ public class MustHaveMatchingValueType : AnalyzerTestFixture valueAndType, + [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerable))] string scalarValuesContainerAttributeArgument, + [CombinatorialMemberData(nameof(FieldOrPropertyDeclarations))] string fieldOrPropertyDeclaration) + { + var testCode = /* lang=c#-test */ $$""" + using BenchmarkDotNet.Attributes; + + namespace DifferentNamespace; + + public class BenchmarkClass + { + [{{dummyAttributeUsage}}Params({{string.Format(scalarValuesContainerAttributeArgument, valueAndType.Value1)}})] + public {{valueAndType.Value2}} {{fieldOrPropertyDeclaration}} + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + ReferenceDummyEnumInDifferentNamespace(); + + await RunAsync(); + } + + [Theory, CombinatorialData] + public async Task Providing_expected_type_using_not_fully_qualified_name_located_in_a_different_namespace_should_not_trigger_diagnostic( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ValuesAndTypesInDifferentNamespace))] ValueTupleDouble valueAndType, + [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerable))] string scalarValuesContainerAttributeArgument, + [CombinatorialMemberData(nameof(FieldOrPropertyDeclarations))] string fieldOrPropertyDeclaration) + { + var testCode = /* lang=c#-test */ $$""" + using DifferentNamespace; + + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [{{dummyAttributeUsage}}Params({{string.Format(scalarValuesContainerAttributeArgument, valueAndType.Value1)}})] + public {{valueAndType.Value2}} {{fieldOrPropertyDeclaration}} + } + """; + + TestCode = testCode; + ReferenceDummyAttribute(); + ReferenceDummyEnumInDifferentNamespace(); await RunAsync(); } @@ -236,6 +451,7 @@ public class BenchmarkClass public {{type}}? {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); ReferenceDummyEnum(); @@ -263,6 +479,7 @@ public class BenchmarkClass public {{valueAndType.Value2}} {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); ReferenceDummyEnum(); @@ -295,6 +512,7 @@ public class BenchmarkClass public {{type}}? {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); ReferenceDummyEnum(); @@ -323,6 +541,7 @@ public class BenchmarkClass public int {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); @@ -346,6 +565,7 @@ public class BenchmarkClass public {{integerValueAndType.Value2}} {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); @@ -358,18 +578,39 @@ public async Task Providing_an_unknown_value_type_should_not_trigger_diagnostic( [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerable))] string scalarValuesContainerAttributeArgument, [CombinatorialMemberData(nameof(FieldOrPropertyDeclarations))] string fieldOrPropertyDeclaration) { - const string expectedFieldOrPropertyType = "int"; - const string valueWithUnknownType = "dummy_literal"; - var testCode = /* lang=c#-test */ $$""" using BenchmarkDotNet.Attributes; public class BenchmarkClass { - [{{dummyAttributeUsage}}Params({{string.Format(scalarValuesContainerAttributeArgument, $"42, {{|#0:{valueWithUnknownType}|}}, 33")}})] - public {{expectedFieldOrPropertyType}} {{fieldOrPropertyDeclaration}} + [{{dummyAttributeUsage}}Params({{string.Format(scalarValuesContainerAttributeArgument, "42, dummy_literal, 33")}})] + public int {{fieldOrPropertyDeclaration}} } """; + + TestCode = testCode; + ReferenceDummyAttribute(); + + DisableCompilerDiagnostics(); + + await RunAsync(); + } + + [Theory, CombinatorialData] + public async Task Providing_an_unknown_type_in_typeof_expression_should_not_trigger_diagnostic( + [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, + [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerable))] string scalarValuesContainerAttributeArgument, + [CombinatorialMemberData(nameof(FieldOrPropertyDeclarations))] string fieldOrPropertyDeclaration) + { + var testCode = /* lang=c#-test */ $$""" + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [{{dummyAttributeUsage}}Params({{string.Format(scalarValuesContainerAttributeArgument, "typeof(int), typeof(dummy_literal)")}})] + public System.Type {{fieldOrPropertyDeclaration}} + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -396,6 +637,7 @@ public class BenchmarkClass public {{expectedFieldOrPropertyType}} {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); @@ -422,6 +664,7 @@ public class BenchmarkClass public {{expectedFieldOrPropertyType}} {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); ReferenceDummyEnum(); @@ -453,6 +696,7 @@ public class BenchmarkClass public {{expectedFieldOrPropertyType}} {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); ReferenceDummyEnum(); @@ -483,6 +727,7 @@ public class BenchmarkClass public {{type}} {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); ReferenceDummyEnum(); @@ -508,6 +753,7 @@ public class BenchmarkClass public {{integerValueAndType.Value2}} {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); AddDefaultExpectedDiagnostic(integerValueAndType.Value1!, integerValueAndType.Value2!, integerValueAndType.Value3!); @@ -519,12 +765,14 @@ public class BenchmarkClass public async Task Providing_an_unexpected_array_value_type_to_params_attribute_should_trigger_diagnostic( [CombinatorialMemberData(nameof(DummyAttributeUsage))] string dummyAttributeUsage, [CombinatorialMemberData(nameof(ValuesAndTypes))] ValueTupleDouble valueAndType, - [CombinatorialMemberData(nameof(ArrayValuesContainerAttributeArgumentWithLocationMarkerEnumerable))] ValueTupleDouble arrayValuesContainerAttributeArgument, + [CombinatorialMemberData(nameof(ArrayValuesContainerAttributeArgumentWithLocationMarkerEnumerableLocal))] ValueTupleDouble arrayValuesContainerAttributeArgument, [CombinatorialMemberData(nameof(FieldOrPropertyDeclarations))] string fieldOrPropertyDeclaration) { const string expectedFieldOrPropertyType = "decimal"; var testCode = /* lang=c#-test */ $$""" + using DifferentNamespace; + using BenchmarkDotNet.Attributes; public class BenchmarkClass @@ -533,9 +781,11 @@ public class BenchmarkClass public {{expectedFieldOrPropertyType}} {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); ReferenceDummyEnum(); + ReferenceDummyEnumInDifferentNamespace(); AddDefaultExpectedDiagnostic( string.Format(arrayValuesContainerAttributeArgument.Value2!, valueAndType.Value1, valueAndType.Value2), @@ -561,6 +811,7 @@ public class BenchmarkClass public decimal {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); @@ -582,6 +833,7 @@ public class BenchmarkClass public object[] {{fieldOrPropertyDeclaration}} } """; + TestCode = testCode; ReferenceDummyAttribute(); @@ -589,9 +841,7 @@ public object[] {{fieldOrPropertyDeclaration}} } public static IEnumerable FieldOrPropertyDeclarations -#pragma warning disable IDE0028 // Simplify collection initialization => new FieldOrPropertyDeclarationsTheoryData(); -#pragma warning restore IDE0028 // Simplify collection initialization public static IEnumerable DummyAttributeUsage => DummyAttributeUsageTheoryData; @@ -720,7 +970,7 @@ public static IEnumerable EmptyValuesAttributeArgument() public static IEnumerable ScalarValuesContainerAttributeArgumentEnumerable => ScalarValuesContainerAttributeArgumentTheoryData(); - public static IEnumerable> ArrayValuesContainerAttributeArgumentWithLocationMarkerEnumerable() + public static IEnumerable ArrayValuesContainerAttributeArgumentEnumerableLocal() { string[] nameColonUsages = [ @@ -734,7 +984,45 @@ public static IEnumerable> ArrayValuesContainer ", Priority = 1" ]; - (string, string)[] attributeUsagesBase = + List arrayValuesContainers = + [ + "{0}new object[] {{{{ new[] {{{{ {{0}} }}}} }}}}{1}", // new object[] { new[] { 42 } } + "{0}new object[] {{{{ new {{1}}[] {{{{ {{0}} }}}} }}}}{1}", // new object[] { new int[] { 42 } } + "{0}[ new[] {{{{ {{0}} }}}} ]{1}", // [ new[] { 42 } ] + "{0}[ new {{1}}[] {{{{ {{0}} }}}} ]{1}", // [ new int[] { 42 } ] + "{0}new object[] {{{{ new {{1}}[] {{{{ }}}} }}}}{1}", // new object[] { new int[] { } } + "{0}[ new {{1}}[] {{{{ }}}} ]{1}", // [ new int[] { } ] + "{0}new object[] {{{{ new {{1}}[0] }}}}{1}", // new object[] { new int[0] } + "{0}[ new {{1}}[0] ]{1}" // [ new int[0] ] + ]; + + foreach (var arrayValuesContainer in arrayValuesContainers) + { + foreach (var nameColonUsage in nameColonUsages) + { + foreach (var priorityNamedParameterUsage in priorityNamedParameterUsages) + { + yield return string.Format(arrayValuesContainer, nameColonUsage, priorityNamedParameterUsage); + } + } + } + } + + public static IEnumerable> ArrayValuesContainerAttributeArgumentWithLocationMarkerEnumerableLocal() + { + string[] nameColonUsages = + [ + "", + "values: " + ]; + + string[] priorityNamedParameterUsages = + [ + "", + ", Priority = 1" + ]; + + (string, string)[] arrayValuesContainers = [ ("{0}new object[] {{{{ {{{{|#0:new[] {{{{ {{0}} }}}}|}}}} }}}}{1}", "new[] {{ {0} }}"), // new object[] { new[] { 42 } } ("{0}new object[] {{{{ {{{{|#0:new {{1}}[] {{{{ {{0}} }}}}|}}}} }}}}{1}", "new {1}[] {{ {0} }}"), // new object[] { new int[] { 42 } } @@ -746,7 +1034,7 @@ public static IEnumerable> ArrayValuesContainer ("{0}[ {{{{|#0:new {{1}}[0]|}}}} ]{1}", "new {1}[0]"), // [ new int[0] ] ]; - foreach (var attributeUsageBase in attributeUsagesBase) + foreach (var arrayValuesContainer in arrayValuesContainers) { foreach (var nameColonUsage in nameColonUsages) { @@ -754,8 +1042,8 @@ public static IEnumerable> ArrayValuesContainer { yield return new ValueTupleDouble { - Value1 = string.Format(attributeUsageBase.Item1, nameColonUsage, priorityNamedParameterUsage), - Value2 = attributeUsageBase.Item2 + Value1 = string.Format(arrayValuesContainer.Item1, nameColonUsage, priorityNamedParameterUsage), + Value2 = arrayValuesContainer.Item2 }; } } @@ -784,9 +1072,17 @@ public static IEnumerable> ArrayValuesContainer (object)"test_object" """, "object" ), ( "typeof(string)", "System.Type" ), + ( "typeof(DummyEnumInDifferentNamespace?)", "System.Type" ), ( "DummyEnum.Value1", "DummyEnum" ), ]; + public static IEnumerable> ValuesAndTypesInDifferentNamespace => + [ + ( "typeof(DummyEnumInDifferentNamespace)", "System.Type" ), + ( "typeof(DummyEnumInDifferentNamespace?)", "System.Type" ), + ( "DummyEnumInDifferentNamespace.Value1", "DummyEnumInDifferentNamespace" ), + ]; + public static IEnumerable> NotConvertibleValuesAndTypes => [ ( "true", "bool" ), @@ -913,9 +1209,7 @@ public string {{fieldOrPropertyDeclaration}} } public static IEnumerable FieldOrPropertyDeclarations -#pragma warning disable IDE0028 // Simplify collection initialization => new FieldOrPropertyDeclarationsTheoryData(); -#pragma warning restore IDE0028 // Simplify collection initialization public static IEnumerable DummyAttributeUsage => DummyAttributeUsageTheoryData; diff --git a/tests/BenchmarkDotNet.Analyzers.Tests/BenchmarkDotNet.Analyzers.Tests.csproj b/tests/BenchmarkDotNet.Analyzers.Tests/BenchmarkDotNet.Analyzers.Tests.csproj index 6df74ed2f7..cc358efe63 100644 --- a/tests/BenchmarkDotNet.Analyzers.Tests/BenchmarkDotNet.Analyzers.Tests.csproj +++ b/tests/BenchmarkDotNet.Analyzers.Tests/BenchmarkDotNet.Analyzers.Tests.csproj @@ -15,10 +15,10 @@ - - + + - + diff --git a/tests/BenchmarkDotNet.Analyzers.Tests/Fixtures/AnalyzerTestFixture.cs b/tests/BenchmarkDotNet.Analyzers.Tests/Fixtures/AnalyzerTestFixture.cs index cb5db4fd5c..d8bba3bf8f 100644 --- a/tests/BenchmarkDotNet.Analyzers.Tests/Fixtures/AnalyzerTestFixture.cs +++ b/tests/BenchmarkDotNet.Analyzers.Tests/Fixtures/AnalyzerTestFixture.cs @@ -1,4 +1,5 @@ using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; @@ -15,7 +16,6 @@ public abstract class AnalyzerTestFixture where TAnalyzer : DiagnosticAnalyzer, new() { private readonly CSharpAnalyzerTest _analyzerTest; - private readonly DiagnosticDescriptor? _ruleUnderTest; private AnalyzerTestFixture(bool assertUniqueSupportedDiagnostics) @@ -54,7 +54,7 @@ protected AnalyzerTestFixture(DiagnosticDescriptor diagnosticDescriptor) : this( { var analyzer = AssertUniqueSupportedDiagnostics(); - // ReSharper disable once ConditionIsAlwaysTrueOrFalse + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (diagnosticDescriptor == null) { Assert.Fail("Diagnostic under test cannot be null when using this constructor"); @@ -201,6 +201,19 @@ public enum DummyEnum """ ); + protected void ReferenceDummyEnumInDifferentNamespace() + => _analyzerTest.TestState.Sources.Add(""" + namespace DifferentNamespace; + + public enum DummyEnumInDifferentNamespace + { + Value1, + Value2, + Value3 + } + """ + ); + protected void ReferenceDummyEnumWithFlagsAttribute() => _analyzerTest.TestState.Sources.Add(""" using System; @@ -237,6 +250,18 @@ public static class Constants """ ); + protected void SetParseOptions(LanguageVersion languageVersion, bool interceptorsNamespaces = false) + { + var parseOptions = new CSharpParseOptions(languageVersion); + + if (interceptorsNamespaces) + { + parseOptions = parseOptions.WithFeatures([ new KeyValuePair(AnalyzerHelper.InterceptorsNamespaces, "") ]); + } + + _analyzerTest.SolutionTransforms.Add((solution, projectId) => solution.WithProjectParseOptions(projectId, parseOptions)); + } + private sealed class InternalAnalyzerTest : CSharpAnalyzerTest { protected override string DefaultTestProjectName => "BenchmarksAssemblyUnderAnalysis"; From 1f3d1dca3ad775c350fcc8eb78c2abd4f6ae25c8 Mon Sep 17 00:00:00 2001 From: Gabriel Bider <1554615+silkfire@users.noreply.github.com> Date: Sat, 15 Nov 2025 02:42:16 +0100 Subject: [PATCH 2/3] Formatting --- .../Attributes/ArgumentsAttributeAnalyzer.cs | 43 ++++---- .../Attributes/ParamsAttributeAnalyzer.cs | 43 ++++---- .../ParamsAttributeAnalyzerTests.cs | 102 +++++++++--------- .../Fixtures/AnalyzerTestFixture.cs | 20 ++-- 4 files changed, 105 insertions(+), 103 deletions(-) diff --git a/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs b/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs index a1c23254cc..52c04f6791 100644 --- a/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs +++ b/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs @@ -284,11 +284,12 @@ void ReportIfNotImplicitlyConvertibleValueTypeDiagnostic(Func _analyzerTest.TestState.Sources.Add(""" - namespace DifferentNamespace; - - public enum DummyEnumInDifferentNamespace - { - Value1, - Value2, - Value3 - } - """ - ); + namespace DifferentNamespace; + + public enum DummyEnumInDifferentNamespace + { + Value1, + Value2, + Value3 + } + """ + ); protected void ReferenceDummyEnumWithFlagsAttribute() => _analyzerTest.TestState.Sources.Add(""" From c4ad960293b785b9bd87d9a1e7093d8bf77ac5b4 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sat, 15 Nov 2025 01:02:38 -0500 Subject: [PATCH 3/3] Formatting --- .../Attributes/ArgumentsAttributeAnalyzer.cs | 22 +- .../Attributes/ParamsAttributeAnalyzer.cs | 12 +- .../ArgumentsAttributeAnalyzerTests.cs | 222 +++++++++--------- .../ParamsAttributeAnalyzerTests.cs | 48 ++-- 4 files changed, 154 insertions(+), 150 deletions(-) diff --git a/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs b/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs index 52c04f6791..e309a579cf 100644 --- a/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs +++ b/src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs @@ -373,19 +373,19 @@ or TypeKind.Enum else if (constantValue is { HasValue: true, Value: null }) { if (!AnalyzerHelper.IsAssignableToField(context.Compilation, - (context.FilterTree.Options as CSharpParseOptions)!.LanguageVersion, - null, - methodParameterTypeSymbol, - valueExpressionString, - constantValue, - null)) + (context.FilterTree.Options as CSharpParseOptions)!.LanguageVersion, + null, + methodParameterTypeSymbol, + valueExpressionString, + constantValue, + null)) { ReportValueTypeMustBeImplicitlyConvertibleDiagnostic( - valueExpressionSyntax.GetLocation(), - valueExpressionString, - expectedValueTypeString, - "null" - ); + valueExpressionSyntax.GetLocation(), + valueExpressionString, + expectedValueTypeString, + "null" + ); } } } diff --git a/src/BenchmarkDotNet.Analyzers/Attributes/ParamsAttributeAnalyzer.cs b/src/BenchmarkDotNet.Analyzers/Attributes/ParamsAttributeAnalyzer.cs index 8e20d2e7ef..c14ae40cfe 100644 --- a/src/BenchmarkDotNet.Analyzers/Attributes/ParamsAttributeAnalyzer.cs +++ b/src/BenchmarkDotNet.Analyzers/Attributes/ParamsAttributeAnalyzer.cs @@ -323,12 +323,12 @@ or TypeKind.Enum else if (constantValue is { HasValue: true, Value: null }) { if (!AnalyzerHelper.IsAssignableToField(context.Compilation, - (context.FilterTree.Options as CSharpParseOptions)!.LanguageVersion, - null, - expectedValueTypeSymbol, - valueExpressionString, - constantValue, - null)) + (context.FilterTree.Options as CSharpParseOptions)!.LanguageVersion, + null, + expectedValueTypeSymbol, + valueExpressionString, + constantValue, + null)) { ReportValueTypeMustBeImplicitlyConvertibleDiagnostic( valueExpressionSyntax.GetLocation(), diff --git a/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs b/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs index 3b6dffe9d5..68fd55d8fa 100644 --- a/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs +++ b/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ArgumentsAttributeAnalyzerTests.cs @@ -408,18 +408,18 @@ public async Task Analyzing_an_arguments_attribute_should_not_throw_an_inconsist [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) { var testCode = /* lang=c#-test */ $$""" - using BenchmarkDotNet.Attributes; - - public class BenchmarkClass - { - [Benchmark] - [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "42")}}] - public void BenchmarkMethod(int a) - { + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "42")}}] + public void BenchmarkMethod(int a) + { - } - } - """; + } + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -435,18 +435,18 @@ public async Task Analyzing_an_arguments_attribute_should_not_throw_an_inconsist [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) { var testCode = /* lang=c#-test */ $$""" - using BenchmarkDotNet.Attributes; - - public class BenchmarkClass - { - [Benchmark] - [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "42")}}] - public void BenchmarkMethod(int a) - { + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "42")}}] + public void BenchmarkMethod(int a) + { - } - } - """; + } + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -510,18 +510,18 @@ public async Task Providing_an_unknown_value_type_should_not_trigger_diagnostic( [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) { var testCode = /* lang=c#-test */ $$""" - using BenchmarkDotNet.Attributes; - - public class BenchmarkClass - { - [Benchmark] - [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "dummy_literal, true")}}] - public void BenchmarkMethod(byte a, bool b) - { + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "dummy_literal, true")}}] + public void BenchmarkMethod(byte a, bool b) + { - } - } - """; + } + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -537,18 +537,18 @@ public async Task Providing_an_unknown_type_in_typeof_expression_should_not_trig [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) { var testCode = /* lang=c#-test */ $$""" - using BenchmarkDotNet.Attributes; - - public class BenchmarkClass - { - [Benchmark] - [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "typeof(int), typeof(dummy_literal)")}}] - public void BenchmarkMethod(System.Type a, System.Type b) - { + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "typeof(int), typeof(dummy_literal)")}}] + public void BenchmarkMethod(System.Type a, System.Type b) + { - } - } - """; + } + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -595,20 +595,20 @@ public async Task Providing_expected_enum_value_type_using_not_fully_qualified_n bool isNullable) { var testCode = /* lang=c#-test */ $$""" - using DifferentNamespace; + using DifferentNamespace; - using BenchmarkDotNet.Attributes; + using BenchmarkDotNet.Attributes; - public class BenchmarkClass - { - [Benchmark] - [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1")}}] - public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) - { + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1")}}] + public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) + { - } - } - """; + } + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -625,20 +625,20 @@ public async Task Providing_expected_enum_value_type_using_not_fully_qualified_n bool isNullable) { var testCode = /* lang=c#-test */ $$""" - using BenchmarkDotNet.Attributes; + using BenchmarkDotNet.Attributes; - namespace DifferentNamespace; - - public class BenchmarkClass - { - [Benchmark] - [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1")}}] - public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) - { + namespace DifferentNamespace; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1")}}] + public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) + { - } - } - """; + } + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -655,19 +655,19 @@ public async Task Providing_expected_enum_value_type_array_using_not_fully_quali bool isNullable) { var testCode = /* lang=c#-test */ $$""" - using DifferentNamespace; + using DifferentNamespace; - using BenchmarkDotNet.Attributes; + using BenchmarkDotNet.Attributes; - public class BenchmarkClass - { - [{{dummyAttributeUsage}}{{string.Format(arrayValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1", $"DummyEnumInDifferentNamespace{(isNullable ? "?" : "")}")}}] - public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) - { + public class BenchmarkClass + { + [{{dummyAttributeUsage}}{{string.Format(arrayValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1", $"DummyEnumInDifferentNamespace{(isNullable ? "?" : "")}")}}] + public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) + { - } - } - """; + } + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -685,19 +685,19 @@ public async Task Providing_expected_enum_value_type_array_using_not_fully_quali bool isNullable) { var testCode = /* lang=c#-test */ $$""" - using BenchmarkDotNet.Attributes; + using BenchmarkDotNet.Attributes; - namespace DifferentNamespace; + namespace DifferentNamespace; - public class BenchmarkClass - { - [{{dummyAttributeUsage}}{{string.Format(arrayValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1", $"DummyEnumInDifferentNamespace{(isNullable ? "?" : "")}")}}] - public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) - { + public class BenchmarkClass + { + [{{dummyAttributeUsage}}{{string.Format(arrayValuesContainerAttributeArgument, "DummyEnumInDifferentNamespace.Value1", $"DummyEnumInDifferentNamespace{(isNullable ? "?" : "")}")}}] + public void BenchmarkMethod(DummyEnumInDifferentNamespace{{(isNullable ? "?" : "")}} a) + { - } - } - """; + } + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -715,20 +715,20 @@ public async Task Providing_expected_type_using_not_fully_qualified_name_located [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) { var testCode = /* lang=c#-test */ $$""" - using BenchmarkDotNet.Attributes; + using BenchmarkDotNet.Attributes; - namespace DifferentNamespace; - - public class BenchmarkClass - { - [Benchmark] - [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, valueAndType.Value1)}}] - public void BenchmarkMethod({{valueAndType.Value2}} a) - { + namespace DifferentNamespace; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, valueAndType.Value1)}}] + public void BenchmarkMethod({{valueAndType.Value2}} a) + { - } - } - """; + } + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -744,20 +744,20 @@ public async Task Providing_expected_type_using_not_fully_qualified_name_located [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerableLocal))] string scalarValuesContainerAttributeArgument) { var testCode = /* lang=c#-test */ $$""" - using DifferentNamespace; + using DifferentNamespace; - using BenchmarkDotNet.Attributes; - - public class BenchmarkClass - { - [Benchmark] - [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, valueAndType.Value1)}}] - public void BenchmarkMethod({{valueAndType.Value2}} a) - { + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [Benchmark] + [{{dummyAttributeUsage}}{{string.Format(scalarValuesContainerAttributeArgument, valueAndType.Value1)}}] + public void BenchmarkMethod({{valueAndType.Value2}} a) + { - } - } - """; + } + } + """; TestCode = testCode; ReferenceDummyAttribute(); diff --git a/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ParamsAttributeAnalyzerTests.cs b/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ParamsAttributeAnalyzerTests.cs index 1a7d62edac..b916c1f5f3 100644 --- a/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ParamsAttributeAnalyzerTests.cs +++ b/tests/BenchmarkDotNet.Analyzers.Tests/AnalyzerTests/Attributes/ParamsAttributeAnalyzerTests.cs @@ -184,14 +184,14 @@ public async Task Analyzing_a_params_attribute_should_not_throw_an_inconsistent_ [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerable))] string scalarValuesContainerAttributeArgument) { var testCode = /* lang=c#-test */ $$""" - using BenchmarkDotNet.Attributes; + using BenchmarkDotNet.Attributes; - public class BenchmarkClass - { - [{{dummyAttributeUsage}}Params({{string.Format(scalarValuesContainerAttributeArgument, "42")}})] - public int {{fieldOrPropertyDeclaration}} - } - """; + public class BenchmarkClass + { + [{{dummyAttributeUsage}}Params({{string.Format(scalarValuesContainerAttributeArgument, "42")}})] + public int {{fieldOrPropertyDeclaration}} + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -207,14 +207,14 @@ public async Task Analyzing_a_params_attribute_should_not_throw_an_inconsistent_ [CombinatorialMemberData(nameof(ScalarValuesContainerAttributeArgumentEnumerable))] string scalarValuesContainerAttributeArgument) { var testCode = /* lang=c#-test */ $$""" - using BenchmarkDotNet.Attributes; + using BenchmarkDotNet.Attributes; - public class BenchmarkClass - { - [{{dummyAttributeUsage}}Params({{string.Format(scalarValuesContainerAttributeArgument, "42")}})] - public int {{fieldOrPropertyDeclaration}} - } - """; + public class BenchmarkClass + { + [{{dummyAttributeUsage}}Params({{string.Format(scalarValuesContainerAttributeArgument, "42")}})] + public int {{fieldOrPropertyDeclaration}} + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -603,14 +603,14 @@ public async Task Providing_an_unknown_type_in_typeof_expression_should_not_trig [CombinatorialMemberData(nameof(FieldOrPropertyDeclarations))] string fieldOrPropertyDeclaration) { var testCode = /* lang=c#-test */ $$""" - using BenchmarkDotNet.Attributes; - - public class BenchmarkClass - { - [{{dummyAttributeUsage}}Params({{string.Format(scalarValuesContainerAttributeArgument, "typeof(int), typeof(dummy_literal)")}})] - public System.Type {{fieldOrPropertyDeclaration}} - } - """; + using BenchmarkDotNet.Attributes; + + public class BenchmarkClass + { + [{{dummyAttributeUsage}}Params({{string.Format(scalarValuesContainerAttributeArgument, "typeof(int), typeof(dummy_literal)")}})] + public System.Type {{fieldOrPropertyDeclaration}} + } + """; TestCode = testCode; ReferenceDummyAttribute(); @@ -841,7 +841,9 @@ public object[] {{fieldOrPropertyDeclaration}} } public static IEnumerable FieldOrPropertyDeclarations +#pragma warning disable IDE0028 // Simplify collection initialization => new FieldOrPropertyDeclarationsTheoryData(); +#pragma warning restore IDE0028 // Simplify collection initialization public static IEnumerable DummyAttributeUsage => DummyAttributeUsageTheoryData; @@ -1209,7 +1211,9 @@ public string {{fieldOrPropertyDeclaration}} } public static IEnumerable FieldOrPropertyDeclarations +#pragma warning disable IDE0028 // Simplify collection initialization => new FieldOrPropertyDeclarationsTheoryData(); +#pragma warning restore IDE0028 // Simplify collection initialization public static IEnumerable DummyAttributeUsage => DummyAttributeUsageTheoryData;