C# keywords are prohibited for benchmark names, print nice error message, fixes #849

This commit is contained in:
Adam Sitnik 2019-01-29 21:49:27 +01:00
Родитель 935ead0dd0
Коммит 20a0110e06
2 изменённых файлов: 46 добавлений и 12 удалений

Просмотреть файл

@ -1,8 +1,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Running;
using Microsoft.CodeAnalysis.CSharp;
namespace BenchmarkDotNet.Validators
{
@ -12,6 +15,8 @@ namespace BenchmarkDotNet.Validators
public static readonly IValidator Default = new CompilationValidator();
private static readonly ImmutableHashSet<string> CsharpKeywords = GetCsharpKeywords().ToImmutableHashSet();
private CompilationValidator() { }
public bool TreatsWarningsAsErrors => true;
@ -29,7 +34,7 @@ namespace BenchmarkDotNet.Validators
.Select(benchmark
=> new ValidationError(
true,
$"Benchmarked method `{benchmark.Descriptor.WorkloadMethod.Name}` contains illegal character(s). Please use `[<Benchmark(Description = \"Custom name\")>]` to set custom display name.",
$"Benchmarked method `{benchmark.Descriptor.WorkloadMethod.Name}` contains illegal character(s) or uses C# keyword. Please use `[<Benchmark(Description = \"Custom name\")>]` to set custom display name.",
benchmark
));
@ -54,23 +59,32 @@ namespace BenchmarkDotNet.Validators
.Select(benchmark
=> new ValidationError(
true,
$"Benchmarked method `{benchmark.Descriptor.WorkloadMethod.DeclaringType.Name}.{benchmark.Descriptor.WorkloadMethod.Name}` is static. Please use instance methods only for benchmarks.",
$"Benchmarked method `{benchmark.Descriptor.WorkloadMethod.Name}` is static. Please use instance methods only for benchmarks.",
benchmark
));
private static bool IsValidCSharpIdentifier(string identifier) // F# allows to use whitespaces as names #479
=> !string.IsNullOrEmpty(identifier)
&& (char.IsLetter(identifier[0]) || identifier[0] == Underscore) // An identifier must start with a letter or an underscore
&& identifier
.Skip(1)
.All(character => char.IsLetterOrDigit(character) || character == Underscore);
&& identifier.Skip(1).All(character => char.IsLetterOrDigit(character) || character == Underscore)
&& !CsharpKeywords.Contains(identifier);
private static bool IsUsingNameUsedInternallyByOurTemplate(string identifier)
=> identifier == "__Overhead";
private static bool HasPrivateGenericArguments(Type type) => type.GetGenericArguments().Any(a => !(a.IsPublic
|| a.IsNestedPublic));
private static bool HasPrivateGenericArguments(Type type) => type.GetGenericArguments().Any(a => !(a.IsPublic || a.IsNestedPublic));
// source: https://stackoverflow.com/a/19562316
private static IEnumerable<string> GetCsharpKeywords()
{
var memberInfos = typeof(SyntaxKind).GetMembers(BindingFlags.Public | BindingFlags.Static);
return from memberInfo in memberInfos
where memberInfo.Name.EndsWith("Keyword")
orderby memberInfo.Name
select memberInfo.Name.Substring(startIndex: 0, length: memberInfo.Name.IndexOf("Keyword")).ToLower();
}
private class BenchmarkMethodEqualityComparer : IEqualityComparer<BenchmarkCase>
{
internal static readonly IEqualityComparer<BenchmarkCase> Instance = new BenchmarkMethodEqualityComparer();

Просмотреть файл

@ -1,6 +1,5 @@
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
@ -33,7 +32,30 @@ namespace BenchmarkDotNet.Tests.Validators
Assert.Contains(errors,
s => s.Equals(
"Benchmarked method `Has Some Whitespaces` contains illegal character(s). Please use `[<Benchmark(Description = \"Custom name\")>]` to set custom display name."));
"Benchmarked method `Has Some Whitespaces` contains illegal character(s) or uses C# keyword. Please use `[<Benchmark(Description = \"Custom name\")>]` to set custom display name."));
}
[Fact]
public void BenchmarkedMethodNameMustNotUseCsharpKeywords()
{
Delegate method = BuildDummyMethod<int>("typeof");
var parameters = new ValidationParameters(
new[]
{
BenchmarkCase.Create(
new Descriptor(
typeof(CompilationValidatorTests),
method.Method),
Job.Dry,
null)
}, new ManualConfig());
var errors = CompilationValidator.Default.Validate(parameters).Select(e => e.Message);
Assert.Contains(errors,
s => s.Equals(
"Benchmarked method `typeof` contains illegal character(s) or uses C# keyword. Please use `[<Benchmark(Description = \"Custom name\")>]` to set custom display name."));
}
[Theory]
@ -115,6 +137,4 @@ namespace BenchmarkDotNet.Tests.Validators
{
internal class InternalNestedClass { }
}
}