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