Contextual Keyword As Identifier
ID |
csharp.contextual_keyword_as_identifier |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
medium |
Remediation Effort |
low |
Resource |
Naming Convention |
Language |
CSharp |
Tags |
keyword, naming, readability |
Description
Reports declarations whose name is a C# contextual keyword: async, await, scoped,
extension or partial by default. Contextual keywords are not
reserved words, so the compiler happily accepts them as ordinary identifiers — but the
meaning of the word then depends entirely on where it appears.
The rule applies to local variables, parameters, fields, constants, properties, events, enum members, methods and type declarations. Only the declaration is reported, so one badly named symbol produces one finding instead of one per reference.
Three uses that look similar are deliberately not reported, because in each of them the word is not an identifier at all:
-
valueandfieldanywhere. Both are contextual only inside a property or event accessor, where they are the implicit parameter and so have no declaration to report; everywhere else they are ordinary, idiomatic names —.NET’s own `IValueConverter.Convert(object value, …)among them, where an interface fixes the name. Add them throughcontextualKeywordsif your team wants them flagged. -
partial,asyncandscopedwritten as modifiers (partial class,async Task,scoped ref). Those parse as keyword tokens, not as names. -
awaitinside anasyncbody, where it is the await operator.
Rationale
A contextual keyword used as a name forces every reader — and every tool — to reconstruct
from the surrounding syntax whether the word is a keyword or an identifier. partial on
its own line could be a modifier or a method call; value in an accessor could be the
implicit parameter or a field that shadows it.
The names are also fragile. The set of contextual keywords grows with each language
version, and a word that is contextual today may become reserved tomorrow, forcing the
code to be escaped as @word or renamed under time pressure.
public int partial() { return 0; } // FLAW — method named after a contextual keyword
public void Configure(int scoped) { } // FLAW — parameter named after a contextual keyword
public class extension { } // FLAW — type named after a contextual keyword
public int Count
{
get { return backing; }
set { backing = value; } // OK — implicit set accessor parameter
}
private int value; // OK — not flagged by default, see above
public partial class Order { } // OK — partial is a modifier here
public async Task LoadAsync(Task pending) // OK — async is a modifier here
{
await pending; // OK — await is an operator here
}
public void Slice(scoped ref int window) { } // OK — scoped is a modifier here
Remediation
Rename the declaration to a word that is not a language keyword and that states what the
value or member represents, for example rename the parameter scoped to window, or the
type extension to FileExtension.
Escaping the name as @scoped makes it compile but keeps the same confusing word at every
use site, so prefer a real rename.
Configuration
properties:
# Contextual keywords rejected as declaration names.
contextualKeywords: [async, await, scoped, extension, partial]
contextualKeywords holds the words rejected as declaration names. Matching is exact and
case-sensitive, and a leading @ (the verbatim-identifier escape) is ignored, so @scoped
matches scoped. Add entries as new contextual keywords appear in the language — value
and field are recognised but off by default — or remove entries a codebase deliberately
accepts.