Custom attribute class does not declare AttributeUsage

ID

vbnet.maintainability.attribute_usage

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Api Design

Language

VB.NET

Description

Reports a class that inherits directly from Attribute or System.Attribute and carries no <AttributeUsage> or <AttributeUsageAttribute> on its declaration, whether or not it is declared inside an explicit Namespace block. Other attributes on the declaration, such as <Obsolete>, do not satisfy the check. Attribute classes that derive from an intermediate base - a framework attribute such as ValidationAttribute, or a project-specific attribute base - are not reported, since the base normally declares the usage for them.

Rationale

Omitting <AttributeUsage> does not leave the question open; it selects defaults, and the defaults are the permissive ones. ValidOn becomes AttributeTargets.All, so the attribute can be applied to an assembly, a parameter or a delegate as readily as to the one kind of declaration the code that consumes it actually inspects. Those applications compile and do nothing, and the developer who wrote one has every reason to believe the feature is switched on. Inherited defaults to True, which is the surprising one: reflection over a derived type reports the attribute its base class carries, so an attribute meant to name or configure a single type is quietly answered on behalf of every subclass, and per-type values end up shared. AllowMultiple defaults to False, which only becomes visible as a compile error to whoever first needs two of them, at which point changing it is a breaking change for consumers that assumed at most one. Declaring the usage explicitly converts each of these from a silent behaviour into either a compile-time error at the misuse or a documented decision.

The following code illustrates the pattern detected by this rule:

Namespace Acme.Diagnostics

    ' FLAGGED: Custom attribute class does not declare AttributeUsage
    Public NotInheritable Class TelemetryNameAttribute
        Inherits Attribute

        Public Sub New(name As String)
            Me.Name = name
        End Sub

Remediation

Declare <AttributeUsage> on the attribute class, naming exactly the AttributeTargets the attribute is designed for - combined with Or when it applies to more than one - and set AllowMultiple and Inherited explicitly rather than relying on their defaults. Inherited should be False unless derived types are genuinely meant to answer for the base.

' Before: applies anywhere, and is inherited by every subclass
Public NotInheritable Class TelemetryNameAttribute
    Inherits Attribute

' After
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method, AllowMultiple:=False, Inherited:=False)>
Public NotInheritable Class TelemetryNameAttribute
    Inherits Attribute

Configuration

This detector does not need any configuration.