Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Added require-const-for-all-caps option to variable-name #2936

Merged
merged 20 commits into from
Mar 12, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/configs/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export const rules = {
true,
"ban-keywords",
"check-format",
"all-caps-for-const",
],
"whitespace": [
true,
Expand Down
56 changes: 42 additions & 14 deletions src/rules/variableNameRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

// tslint:disable object-literal-sort-keys

import { hasModifier } from "tsutils";
import * as tsutils from "tsutils";
import * as ts from "typescript";

import * as Lint from "../index";
Expand All @@ -31,6 +31,7 @@ const OPTION_LEADING_UNDERSCORE = "allow-leading-underscore";
const OPTION_TRAILING_UNDERSCORE = "allow-trailing-underscore";
const OPTION_BAN_KEYWORDS = "ban-keywords";
const OPTION_CHECK_FORMAT = "check-format";
const OPTION_ALL_CAPS_FOR_CONST = "all-caps-for-const";
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
const OPTION_ALLOW_PASCAL_CASE = "allow-pascal-case";
const OPTION_ALLOW_SNAKE_CASE = "allow-snake-case";

Expand All @@ -39,11 +40,12 @@ export class Rule extends Lint.Rules.AbstractRule {
ruleName: "variable-name",
description: "Checks variable names for various errors.",
optionsDescription: Lint.Utils.dedent`
Five arguments may be optionally provided:
Six arguments may be optionally provided:

* \`"${OPTION_CHECK_FORMAT}"\`: allows only lowerCamelCased or UPPER_CASED variable names
* \`"${OPTION_LEADING_UNDERSCORE}"\` allows underscores at the beginning (only has an effect if "check-format" specified)
* \`"${OPTION_TRAILING_UNDERSCORE}"\` allows underscores at the end. (only has an effect if "check-format" specified)
* \`"${OPTION_ALL_CAPS_FOR_CONST}"\`: enforces that all variables with UPPER_CASED names should be \`const\`.
* \`"${OPTION_ALLOW_PASCAL_CASE}"\` allows PascalCase in addition to lowerCamelCase.
* \`"${OPTION_ALLOW_SNAKE_CASE}"\` allows snake_case in addition to lowerCamelCase.
* \`"${OPTION_BAN_KEYWORDS}"\`: disallows the use of certain TypeScript keywords as variable or parameter names.
Expand All @@ -62,7 +64,7 @@ export class Rule extends Lint.Rules.AbstractRule {
],
},
minLength: 0,
maxLength: 5,
maxLength: 6,
},
optionExamples: [[true, "ban-keywords", "check-format", "allow-leading-underscore"]],
type: "style",
Expand All @@ -71,6 +73,8 @@ export class Rule extends Lint.Rules.AbstractRule {

public static KEYWORD_FAILURE = "variable name clashes with keyword/type";

public static FAILURE_STRING_CONST = "Only `const` variables may be UPPER_CASE.";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, parseOptions(this.ruleArguments));
}
Expand All @@ -81,6 +85,7 @@ interface Options {
checkFormat: boolean;
leadingUnderscore: boolean;
trailingUnderscore: boolean;
allCapsForConst: boolean;
allowPascalCase: boolean;
allowSnakeCase: boolean;
}
Expand All @@ -92,6 +97,7 @@ function parseOptions(ruleArguments: string[]): Options {
checkFormat: !banKeywords || hasOption(OPTION_CHECK_FORMAT),
leadingUnderscore: hasOption(OPTION_LEADING_UNDERSCORE),
trailingUnderscore: hasOption(OPTION_TRAILING_UNDERSCORE),
allCapsForConst: hasOption(OPTION_ALL_CAPS_FOR_CONST),
allowPascalCase: hasOption(OPTION_ALLOW_PASCAL_CASE),
allowSnakeCase: hasOption(OPTION_ALLOW_SNAKE_CASE),
};
Expand Down Expand Up @@ -120,28 +126,35 @@ function walk(ctx: Lint.WalkContext<Options>): void {

case ts.SyntaxKind.VariableStatement:
// skip 'declare' keywords
if (hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword)) {
if (tsutils.hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword)) {
return;
}
break;

case ts.SyntaxKind.Parameter:
case ts.SyntaxKind.PropertyDeclaration:
case ts.SyntaxKind.VariableDeclaration: {
const { name, initializer } = node as ts.ParameterDeclaration | ts.PropertyDeclaration | ts.VariableDeclaration;
if (name.kind === ts.SyntaxKind.Identifier) {
handleVariableNameFormat(name, initializer);
// do not check property declarations for keywords, they are allowed to be keywords
if (node.kind !== ts.SyntaxKind.PropertyDeclaration) {
handleVariableNameKeyword(name);
}
}
}
handleDeclaredVariable(node as ts.ParameterDeclaration | ts.PropertyDeclaration);
break;

case ts.SyntaxKind.VariableDeclaration:
handleVariableDeclaration(node as ts.VariableDeclaration);
}

return ts.forEachChild(node, cb);
});

function handleDeclaredVariable(node: ts.ParameterDeclaration | ts.PropertyDeclaration | ts.VariableDeclaration): void {
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
const { name, initializer } = node;

if (name.kind === ts.SyntaxKind.Identifier) {
handleVariableNameFormat(name, initializer);
// do not check property declarations for keywords, they are allowed to be keywords
if (node.kind !== ts.SyntaxKind.PropertyDeclaration) {
handleVariableNameKeyword(name);
}
}
}

function handleVariableNameFormat(name: ts.Identifier, initializer?: ts.Expression): void {
if (!options.checkFormat) {
return;
Expand All @@ -163,6 +176,21 @@ function walk(ctx: Lint.WalkContext<Options>): void {
}
}

function handleVariableDeclaration(node: ts.VariableDeclaration): void {
handleDeclaredVariable(node);

if (!ctx.options.allCapsForConst || tsutils.isBindingPattern(node.name)) {
return;
}

const declarationList = node.parent!;
const text = node.name.text;

if (isUpperCase(text) && !tsutils.isNodeFlagSet(declarationList, ts.NodeFlags.Const)) {
ctx.addFailureAtNode(node, Rule.FAILURE_STRING_CONST);
}
}

function formatFailure(): string {
let failureMessage = "variable name must be in lowerCamelCase";
if (options.allowPascalCase) {
Expand Down
34 changes: 34 additions & 0 deletions test/rules/variable-name/all-caps-for-const/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var snake_case_var;
~~~~~~~~~~~~~~ [regular-case]

var camelCaseVar;

var PascalCaseVar;
~~~~~~~~~~~~~ [regular-case]

var UPPER_CASE_VAR;
~~~~~~~~~~~~~~ [err]

let snake_case_let;
~~~~~~~~~~~~~~ [regular-case]

let camelCaseLet;

let PascalCaseLet;
~~~~~~~~~~~~~ [regular-case]

let UPPER_CASE_LET;
~~~~~~~~~~~~~~ [err]

const snake_case_const;
~~~~~~~~~~~~~~~~ [regular-case]

const camelCaseConst;

const PascalCaseConst;
~~~~~~~~~~~~~~~ [regular-case]

JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
const UPPER_CASE_CONST;

[regular-case]: variable name must be in lowerCamelCase or UPPER_CASE
[err]: Only `const` variables may be UPPER_CASE.
5 changes: 5 additions & 0 deletions test/rules/variable-name/all-caps-for-const/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"variable-name": [true, "all-caps-for-const"]
}
}