Skip to content

Commit

Permalink
Add validation for Params* attributes on init-only properties, fix #1877
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyAkinshin committed Mar 6, 2023
1 parent 4b6e83d commit b846d57
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs
Expand Up @@ -19,6 +19,20 @@ internal static class ReflectionExtensions

internal static bool IsNullable(this Type type) => Nullable.GetUnderlyingType(type) != null;

public static bool IsInitOnly(this PropertyInfo propertyInfo)
{
var setMethodReturnParameter = propertyInfo.SetMethod?.ReturnParameter;
if (setMethodReturnParameter == null)
return false;

var isExternalInitType = typeof(System.Runtime.CompilerServices.Unsafe).Assembly
.GetType("System.Runtime.CompilerServices.IsExternalInit");
if (isExternalInitType == null)
return false;

return setMethodReturnParameter.GetRequiredCustomModifiers().Contains(isExternalInitType);
}

/// <summary>
/// returns type name which can be used in generated C# code
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/BenchmarkDotNet/Validators/ParamsValidator.cs
Expand Up @@ -46,6 +46,12 @@ private IEnumerable<ValidationError> Validate(Type type)
yield return new ValidationError(TreatsWarningsAsErrors,
$"Unable to use {name} with {attributeString} because it's a {modifier} field. Please, remove the {modifier} modifier.");
}

if (memberInfo is PropertyInfo propertyInfo && propertyInfo.IsInitOnly())
{
yield return new ValidationError(TreatsWarningsAsErrors,
$"Unable to use {name} with {attributeString} because it's init-only. Please, provide a public setter.");
}
}
}
}
Expand Down
26 changes: 26 additions & 0 deletions tests/BenchmarkDotNet.Tests/Validators/ParamsValidatorTests.cs
Expand Up @@ -176,5 +176,31 @@ public class PropMultiple4 : Base
[ParamsSource(nameof(Source))]
public bool Input { get; set; }
}

#if NET5_0_OR_GREATER

[Fact] public void InitOnly1Test() => Check<InitOnly1>(nameof(InitOnly1.Input), "init-only", P);
[Fact] public void InitOnly2Test() => Check<InitOnly2>(nameof(InitOnly2.Input), "init-only", Pa);
[Fact] public void InitOnly3Test() => Check<InitOnly3>(nameof(InitOnly3.Input), "init-only", Ps);

public class InitOnly1 : Base
{
[Params(false, true)]
public bool Input { get; init; }
}

public class InitOnly2 : Base
{
[ParamsAllValues]
public bool Input { get; init; }
}

public class InitOnly3 : Base
{
[ParamsSource(nameof(Source))]
public bool Input { get; init; }
}

#endif
}
}

0 comments on commit b846d57

Please sign in to comment.