Attribute type name does not end in Attribute

ID

vbnet.maintainability.attribute_suffix

Severity

info

Remediation Complexity

trivial

Remediation Risk

medium

Remediation Effort

low

Resource

Naming

Language

VB.NET

Description

Reports a type that derives from System.Attribute, directly or through another attribute type, whose own name does not end in Attribute - Marker rather than MarkerAttribute, Audited rather than AuditedAttribute. The base type is recognized from the Inherits clause, so Inherits Attribute, Inherits System.Attribute and Inherits ValidationAttribute are all treated as making the declaring type an attribute. An abstract intermediate class named …​AttributeBase is an accepted idiom and is not reported. This is a convention rule and is reported at informational severity.

Rationale

The Attribute suffix is not decoration: the language depends on it. When an attribute is applied, the compiler lets the suffix be omitted at the use site, so <Marker> and <MarkerAttribute> both resolve to the same type - which means a type named Marker is applied as <Marker> and is indistinguishable in source from an attribute named MarkerAttribute that was written the short way. The consequences are concrete. Two types in scope, Marker and MarkerAttribute, make <Marker> ambiguous and the compiler rejects it, so a later, correctly named addition to the namespace breaks existing code. Reflection code that looks a type up by name has to guess which spelling was used. And a reader scanning a namespace listing cannot tell which types are attributes and which are ordinary classes, even though the two are used in completely different positions - one decorates declarations, the other is instantiated. Since the suffix is what every consumer sees and what the shortening rule assumes, adding it later is a breaking change to any code that already spells the name out in full.

The following code illustrates the pattern detected by this rule:

Namespace Metadata

    ' BAD: derives from Attribute but the name does not say so.
    ' FLAGGED: Attribute type name does not end in Attribute
    Public Class Marker
        Inherits Attribute

        Public Property Reason As String
    End Class

Remediation

Rename the type so that it ends in Attribute: Marker becomes MarkerAttribute, Audited becomes AuditedAttribute and RequiresRole becomes RequiresRoleAttribute. Use sites need no change, because the compiler allows the suffix to be omitted when an attribute is applied, so <Marker> keeps working and is now unambiguous; only code that names the type explicitly - a GetCustomAttribute(Of MarkerAttribute)() call, a GetType(Marker) expression or a string lookup by type name - has to be updated, and the compiler or the IDE rename refactoring will find those for you. While renaming, also mark the type NotInheritable and give it an <AttributeUsage> declaring the targets it is valid on, since both are expected of an attribute and are usually missing together with the suffix.

Configuration

This detector does not need any configuration.