Declared name is a VB.NET keyword

ID

vbnet.maintainability.contextual_keyword_as_identifier

Severity

low

Remediation Complexity

medium

Remediation Risk

medium

Remediation Effort

low

Resource

Naming

Language

VB.NET

Description

Reports a declaration - a type, a method, a field or a local variable - whose name is a VB.NET keyword. Two cases are covered. The first is a reserved word made usable as a name by escaping it with brackets, as in Private [Class] As Integer or Dim [Stop] As Integer. The second is a contextual keyword used bare, which the compiler accepts but which carries meaning elsewhere in the language: the query keywords From, Into, Where, Aggregate, Distinct, Ascending and Descending, the asynchronous and iterator keywords Async, Await, Iterator and Yield, and the compilation keywords Explicit, Infer, Strict, Unicode and Preserve. Names that merely contain a keyword, such as _descendingOrder or awaited, are not reported.

Rationale

A bracket-escaped name means the code needed the compiler’s permission to exist: [Class] is only legal because the brackets suppress the keyword, and every reference to it must carry them too, so the brackets propagate through the codebase and any developer who forgets them gets a syntax error whose message points at the keyword rather than at the name. A contextual keyword used bare is accepted without brackets, which makes it more dangerous rather than less, because the collision surfaces later and somewhere else. A variable named await sits in a method that cannot be converted to Async without renaming it first; a local named from or where inside a method that later gains a LINQ query puts the reader in the position of parsing From x In from to work out which token is the operator; a member named Aggregate or Distinct on a type that is also enumerable shadows the query operator readers expect, so items.Distinct() no longer means what it means everywhere else. In all of these the name has taken a word the language already uses, so the cost is paid by every reader who has to decide which meaning is in play - and by whoever later wants to use the language feature the word belongs to.

The following code illustrates the pattern detected by this rule:

Namespace Reporting

    ' BAD: a type named after a query keyword.
    ' FLAGGED: Declared name is a VB.NET keyword
    Public Class Distinct

        Public Property Column As String
    End Class

Remediation

Rename the declaration to a word the language does not use. For a bracket-escaped name, drop the brackets along with the keyword: [Class] becomes Category, Kind or TypeName depending on what it holds, and [Stop] becomes StopRequested or Halt; removing the brackets everywhere is part of the rename and the compiler will point out any reference you miss. For a contextual keyword, qualify it into a phrase rather than abbreviating it away: descending becomes isDescending or sortDescending, await becomes pendingCount or whatever the value actually is, and a method named Aggregate becomes Summarize or Combine. If a name genuinely must match an external contract - an interop signature, a serialized field name or a column - keep the contract at the boundary and give the .NET declaration a clean name, mapping the two with an attribute such as <DataMember(Name := "class")> rather than escaping the keyword in your own code. Use the rename refactoring in the IDE so every reference is updated in the same step.

Configuration

This detector does not need any configuration.