Skip to content

Commit

Permalink
MemberInfo.HasAttribute with a predicate was missing an implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
dennisdoomen committed Nov 26, 2024
1 parent 3c395ff commit 840ecd3
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 8 deletions.
22 changes: 14 additions & 8 deletions src/Reflectify/Reflectify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
Expand Down Expand Up @@ -475,29 +474,36 @@ public static Type NullableOrActualType(this Type type)

internal static class MemberInfoExtensions
{
public static bool HasAttribute<TAttribute>(this MemberInfo type)
public static bool HasAttribute<TAttribute>(this MemberInfo member)
where TAttribute : Attribute
{
// Do not use MemberInfo.IsDefined
// There is an issue with PropertyInfo and EventInfo preventing the inherit option to work.
// https://github.com/dotnet/runtime/issues/30219
return Attribute.IsDefined(type, typeof(TAttribute), inherit: false);
return Attribute.IsDefined(member, typeof(TAttribute), inherit: false);
}

public static bool HasAttribute<TAttribute>(this MemberInfo type,
Expression<Func<TAttribute, bool>> isMatchingAttributePredicate)
/// <summary>
/// Determines whether the member has an attribute of the specified type that matches the predicate.
/// </summary>
public static bool HasAttribute<TAttribute>(this MemberInfo member, Func<TAttribute, bool> predicate)
where TAttribute : Attribute
{
return false;
if (predicate is null)
{
throw new ArgumentNullException(nameof(predicate));
}

return member.GetCustomAttributes<TAttribute>().Any(a => predicate(a));
}

public static bool HasAttributeInHierarchy<TAttribute>(this MemberInfo type)
public static bool HasAttributeInHierarchy<TAttribute>(this MemberInfo member)
where TAttribute : Attribute
{
// Do not use MemberInfo.IsDefined
// There is an issue with PropertyInfo and EventInfo preventing the inherit option to work.
// https://github.com/dotnet/runtime/issues/30219
return Attribute.IsDefined(type, typeof(TAttribute), inherit: true);
return Attribute.IsDefined(member, typeof(TAttribute), inherit: true);
}
}

Expand Down
12 changes: 12 additions & 0 deletions tests/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
# Reason: We don't care about this in tests
dotnet_diagnostic.CA1034.severity = none

# Purpose: Specify StringComparison for clarity
# Reason: We don't care about this in tests
dotnet_diagnostic.CA1307.severity = none

# Purpose: The behavior of 'string. StartsWith(string)' could vary based on the current user's locale settings
# Reason: We don't care about this in tests
dotnet_diagnostic.CA1310.severity = none

# Purpose: Type is a static holder type but is neither static nor NotInheritable
# Reason: We don't care about this in tests
dotnet_diagnostic.CA1052.severity = none
Expand Down Expand Up @@ -48,6 +56,10 @@ dotnet_diagnostic.AV1010.severity = none
# Reason: Not needed for tests
dotnet_diagnostic.AV1564.severity = none

# Purpose: Use an overload of 'StartsWith' that has a StringComparison parameter
# Reason: Duplicate of CA1310
dotnet_diagnostic.MA0074.severity = none

# Purpose: Class is never instantiated (non-private accessibility)
# Reason: We don't instantiate test classes directly
resharper_class_never_instantiated_global_highlighting=none
61 changes: 61 additions & 0 deletions tests/Reflectify.Specs/MemberInfoExtensionsSpecs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using FluentAssertions;
using Xunit;

namespace Reflectify.Specs;

public class MemberInfoExtensionsSpecs
{
[Fact]
public void Can_determine_a_method_has_an_attribute()
{
// Arrange
var member = typeof(ClassWithAttributedMember).GetMethod("Method");

// Act / Assert
member.HasAttribute<ObsoleteAttribute>().Should().BeTrue();
}

[Fact]
public void Can_determine_a_method_has_an_attribute_using_a_specific_predicate()
{
// Arrange
var member = typeof(ClassWithAttributedMember).GetMethod("Method");

// Act / Assert
member.HasAttribute<ObsoleteAttribute>(attribute =>
attribute.Message!.StartsWith("Specific")).Should().BeTrue();
}

[Fact]
public void The_predicate_must_not_be_null()
{
// Arrange
var member = typeof(ClassWithAttributedMember).GetMethod("Method");

// Act
var act = () => member.HasAttribute<ObsoleteAttribute>(null).Should().BeTrue();

// Assert
act.Should().Throw<ArgumentNullException>().WithMessage("*predicate*");
}

[Fact]
public void Can_determine_a_method_has_an_attribute_that_does_not_meet_a_predicate()
{
// Arrange
var member = typeof(ClassWithAttributedMember).GetMethod("Method");

// Act / Assert
member.HasAttribute<ObsoleteAttribute>(predicate =>
predicate.Message.Contains("*Other*")).Should().BeFalse();
}

private class ClassWithAttributedMember
{
[Obsolete("Specific reason")]
public void Method()
{
}
}
}

0 comments on commit 840ecd3

Please sign in to comment.