Search code examples
roslynroslyn-code-analysis

Roslyn: is ISymbol.GetAttributes returns inherited attributes


Roslyn has ISymbol interface with various useful methods. I'm trying to get all class attributes via ISymbol.GetAttributes. This is a documentation link:

https://learn.microsoft.com/de-de/dotnet/api/microsoft.codeanalysis.isymbol.getattributes?view=roslyn-dotnet

As we can see there is no indication if this method returns inherited attributes (attributes from base classes) or not. So this is the first questions. The second question - why there is no mentions about that in documentation?


Solution

  • I don't know why there's no mention of it in the documentation, and think it should be mentioned there.

    Since I was facing the same issue, I tested it, and it will not return inherited attributes. You can use these extension methods to get all attributes including the inherited ones:

    public static IEnumerable<AttributeData> GetAttributesWithInherited(this INamedTypeSymbol typeSymbol) {
        foreach (var attribute in typeSymbol.GetAttributes()) {
            yield return attribute;
        }
    
        var baseType = typeSymbol.BaseType;
        while (baseType != null) {
            foreach (var attribute in baseType.GetAttributes()) {
                if (IsInherited(attribute)) {
                    yield return attribute;
                }
            }
    
            baseType = baseType.BaseType;
        }
    }
    
    private static bool IsInherited(this AttributeData attribute) {
        if (attribute.AttributeClass == null) {
            return false;
        }
    
        foreach (var attributeAttribute in attribute.AttributeClass.GetAttributes()) {
            var @class = attributeAttribute.AttributeClass;
            if (@class != null && @class.Name == nameof(AttributeUsageAttribute) &&
                @class.ContainingNamespace?.Name == "System") {
                foreach (var kvp in attributeAttribute.NamedArguments) {
                    if (kvp.Key == nameof(AttributeUsageAttribute.Inherited)) {
                        return (bool) kvp.Value.Value!;
                    }
                }
    
                // Default value of Inherited is true
                return true;
            }
        }
    
        // An attribute without an `AttributeUsage` attribute will also default to being inherited.
        return true;
    }