From 4f35de90d759afc6fa7152242075dea8fe132a9e Mon Sep 17 00:00:00 2001 From: Paul Jolly Date: Wed, 14 Oct 2015 14:06:48 +0100 Subject: [PATCH] Rule to prevent certain keywords/types from being used as variable names --- README.md | 1 + docs/sample.tslint.json | 121 +++++++++++------- src/rules/noKeywordNamedVariablesRule.ts | 58 +++++++++ src/tsconfig.json | 3 +- .../rules/nokeywordnamedvariables_1.test.ts | 23 ++++ .../rules/nokeywordnamedvariables_2.test.ts | 8 ++ .../rules/nokeywordnamedvariables_3.test.ts | 17 +++ .../rules/nokeywordnamedvariables_4.test.ts | 8 ++ .../rules/nokeywordnamedvariables_5.test.ts | 8 ++ .../rules/noKeywordNamedVariablesRuleTests.ts | 119 +++++++++++++++++ test/tsconfig.json | 1 + 11 files changed, 320 insertions(+), 47 deletions(-) create mode 100644 src/rules/noKeywordNamedVariablesRule.ts create mode 100644 test/files/rules/nokeywordnamedvariables_1.test.ts create mode 100644 test/files/rules/nokeywordnamedvariables_2.test.ts create mode 100644 test/files/rules/nokeywordnamedvariables_3.test.ts create mode 100644 test/files/rules/nokeywordnamedvariables_4.test.ts create mode 100644 test/files/rules/nokeywordnamedvariables_5.test.ts create mode 100644 test/rules/noKeywordNamedVariablesRuleTests.ts diff --git a/README.md b/README.md index d727d1eac92..aa3248de434 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,7 @@ A sample configuration file with all options is available [here](https://github. * `no-eval` disallows `eval` function invocations. * `no-inferrable-types` disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean * `no-internal-module` disallows internal `module`, use `namespace` instead. +* `no-keyword-named-variables` disallows the use of certain TypeScript keywords (`any`, `Number`, `number`, `String`, `string`, `Boolean`, `boolean`, `undefined`) as variable or parameter names * `no-require-imports` disallows require() style imports * `no-string-literal` disallows object access via string literals. * `no-switch-case-fall-through` disallows falling through case statements. diff --git a/docs/sample.tslint.json b/docs/sample.tslint.json index 193788dfcaa..f97bade7c09 100644 --- a/docs/sample.tslint.json +++ b/docs/sample.tslint.json @@ -1,52 +1,64 @@ { "rules": { - "align": [true, - "parameters", - "arguments", - "statements"], + "align": [ + true, + "parameters", + "arguments", + "statements" + ], "ban": false, "class-name": true, - "comment-format": [true, - "check-space", - "check-lowercase" + "comment-format": [ + true, + "check-space", + "check-lowercase" ], "curly": true, "eofline": true, "forin": true, - "indent": [true, "spaces"], + "indent": [ + true, + "spaces" + ], "interface-name": true, "jsdoc-format": true, "label-position": true, "label-undefined": true, - "max-line-length": [true, 140], + "max-line-length": [ + true, + 140 + ], "member-access": true, - "member-ordering": [true, - "public-before-private", - "static-before-instance", - "variables-before-functions" + "member-ordering": [ + true, + "public-before-private", + "static-before-instance", + "variables-before-functions" ], "no-any": false, "no-arg": true, "no-bitwise": true, "no-conditional-assignment": true, - "no-console": [true, - "debug", - "info", - "time", - "timeEnd", - "trace" + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" ], "no-construct": true, "no-constructor-vars": true, "no-debugger": true, "no-duplicate-key": true, - "no-shadowed-variable": true, "no-duplicate-variable": true, "no-empty": true, "no-eval": true, "no-inferrable-types": false, "no-internal-module": true, + "no-keyword-named-variables": true, "no-require-imports": true, + "no-shadowed-variable": true, "no-string-literal": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, @@ -57,46 +69,63 @@ "no-var-keyword": true, "no-var-requires": true, "object-literal-sort-keys": true, - "one-line": [true, - "check-open-brace", - "check-catch", - "check-else", - "check-whitespace" + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "quotemark": [ + true, + "double", + "avoid-escape" ], - "quotemark": [true, "double", "avoid-escape"], "radix": true, "semicolon": true, "switch-default": true, - "trailing-comma": [true, { + "trailing-comma": [ + true, + { "multiline": "always", "singleline": "never" - }], - "triple-equals": [true, "allow-null-check"], - "typedef": [true, - "call-signature", - "parameter", - "property-declaration", - "variable-declaration", - "member-variable-declaration" + } + ], + "triple-equals": [ + true, + "allow-null-check" ], - "typedef-whitespace": [true, { + "typedef": [ + true, + "call-signature", + "parameter", + "property-declaration", + "variable-declaration", + "member-variable-declaration" + ], + "typedef-whitespace": [ + true, + { "call-signature": "nospace", "index-signature": "nospace", "parameter": "nospace", "property-declaration": "nospace", "variable-declaration": "nospace" - }], - "use-strict": [true, - "check-module", - "check-function" + } + ], + "use-strict": [ + true, + "check-module", + "check-function" ], "variable-name": false, - "whitespace": [true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" ] } } diff --git a/src/rules/noKeywordNamedVariablesRule.ts b/src/rules/noKeywordNamedVariablesRule.ts new file mode 100644 index 00000000000..346a35126b8 --- /dev/null +++ b/src/rules/noKeywordNamedVariablesRule.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2014 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as Lint from "../lint"; +import * as ts from "typescript"; + +const BAD_NAMES = [ "any", "Number", "number", "String", "string", "Boolean", "boolean", "undefined" ]; + +export class Rule extends Lint.Rules.AbstractRule { + public static FAILURE_STRING = "variable name clashes with keyword/type: "; + + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithWalker(new NoKeywordNamedVariablesWalker(sourceFile, this.getOptions())); + } +} + +class NoKeywordNamedVariablesWalker extends Lint.RuleWalker { + public visitBindingElement(node: ts.BindingElement) { + if (node.name.kind === ts.SyntaxKind.Identifier) { + this.handleVariableName( node.name); + } + super.visitBindingElement(node); + } + + public visitParameterDeclaration(node: ts.ParameterDeclaration) { + if (node.name.kind === ts.SyntaxKind.Identifier) { + this.handleVariableName( node.name); + } + super.visitParameterDeclaration(node); + } + + public visitVariableDeclaration(node: ts.VariableDeclaration) { + if (node.name.kind === ts.SyntaxKind.Identifier) { + this.handleVariableName( node.name); + } + super.visitVariableDeclaration(node); + } + + private handleVariableName(name: ts.Identifier) { + const variableName = name.text; + + if(BAD_NAMES.indexOf(variableName) !== -1) { + this.addFailure(this.createFailure(name.getStart(), name.getWidth(), Rule.FAILURE_STRING)); + } + } +} diff --git a/src/tsconfig.json b/src/tsconfig.json index 5591e01236e..f331f0341cc 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -32,8 +32,8 @@ "./rules/noConditionalAssignmentRule.ts", "./rules/noConsecutiveBlankLinesRule.ts", "./rules/noConsoleRule.ts", - "./rules/noConstructRule.ts", "./rules/noConstructorVarsRule.ts", + "./rules/noConstructRule.ts", "./rules/noDebuggerRule.ts", "./rules/noDuplicateKeyRule.ts", "./rules/noDuplicateVariableRule.ts", @@ -41,6 +41,7 @@ "./rules/noEvalRule.ts", "./rules/noInferrableTypesRule.ts", "./rules/noInternalModuleRule.ts", + "./rules/noKeywordNamedVariablesRule", "./rules/noRequireImportsRule.ts", "./rules/noShadowedVariableRule.ts", "./rules/noStringLiteralRule.ts", diff --git a/test/files/rules/nokeywordnamedvariables_1.test.ts b/test/files/rules/nokeywordnamedvariables_1.test.ts new file mode 100644 index 00000000000..0c03578a97b --- /dev/null +++ b/test/files/rules/nokeywordnamedvariables_1.test.ts @@ -0,0 +1,23 @@ +let any = 1; +let Number = 2; +let number = 3; +let String = 4; +let string = 5; +let Boolean = 6; +let boolean = 7; +let undefined = 8; + +let Any = 1; +let anyName = 2; +let NumbeR = 3 +let NumberEg = 4; +let numbeR = 5; +let numberEg = 6; +let StrinG = 7; +let StringEg = 8; +let BooleaN = 9; +let BooleanEg = 10; +let booleaN = 11; +let booleanEg = 12; +let undefineD = 13; +let undefinedEg = 14; diff --git a/test/files/rules/nokeywordnamedvariables_2.test.ts b/test/files/rules/nokeywordnamedvariables_2.test.ts new file mode 100644 index 00000000000..89c60848242 --- /dev/null +++ b/test/files/rules/nokeywordnamedvariables_2.test.ts @@ -0,0 +1,8 @@ +let any: any; +let Number: Number; +let number: number; +let String: String; +let string: string; +let Boolean: Boolean; +let boolean: boolean; +let undefined: any; diff --git a/test/files/rules/nokeywordnamedvariables_3.test.ts b/test/files/rules/nokeywordnamedvariables_3.test.ts new file mode 100644 index 00000000000..cef32c6e10d --- /dev/null +++ b/test/files/rules/nokeywordnamedvariables_3.test.ts @@ -0,0 +1,17 @@ +function F1(any) {} +function F2(Number) {} +function F3(number) {} +function F4(String) {} +function F5(string) {} +function F6(Boolean) {} +function F7(boolean) {} +function F8(undefined) {} + +function G1(any: any) {} +function G2(Number: Number) {} +function G3(number: number) {} +function G4(String: String) {} +function G5(string: string) {} +function G6(Boolean: Boolean) {} +function G7(boolean: boolean) {} +function G8(undefined: any) {} diff --git a/test/files/rules/nokeywordnamedvariables_4.test.ts b/test/files/rules/nokeywordnamedvariables_4.test.ts new file mode 100644 index 00000000000..10d8cbbf740 --- /dev/null +++ b/test/files/rules/nokeywordnamedvariables_4.test.ts @@ -0,0 +1,8 @@ +let [ any ] = [ 1 ]; +let [ Number ] = [ 2 ]; +let [ number ] = [ 3 ]; +let [ String ] = [ 4 ]; +let [ string ] = [ 5 ]; +let [ Boolean ] = [ 6 ]; +let [ boolean ] = [ 7 ]; +let [ undefined ] = [ 8 ]; diff --git a/test/files/rules/nokeywordnamedvariables_5.test.ts b/test/files/rules/nokeywordnamedvariables_5.test.ts new file mode 100644 index 00000000000..1429f3597d1 --- /dev/null +++ b/test/files/rules/nokeywordnamedvariables_5.test.ts @@ -0,0 +1,8 @@ +let { any } = { any: 1 }; +let { Number } = { Number: 1 }; +let { number } = { number: 1 }; +let { String } = { String: 1 }; +let { string } = { string: 1 }; +let { Boolean } = { Boolean: 1 }; +let { boolean } = { boolean: 1 }; +let { undefined } = { undefined: 1 }; diff --git a/test/rules/noKeywordNamedVariablesRuleTests.ts b/test/rules/noKeywordNamedVariablesRuleTests.ts new file mode 100644 index 00000000000..4ec354e36f7 --- /dev/null +++ b/test/rules/noKeywordNamedVariablesRuleTests.ts @@ -0,0 +1,119 @@ +/* + * Copyright 2013 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as Lint from "../lint"; + +describe("", () => { + const VariableNameRule = Lint.Test.getRule("no-keyword-named-variables"); + const failureString = VariableNameRule.FAILURE_STRING; + + it("checks assigned let variables", () => { + const fileName = "rules/nokeywordnamedvariables_1.test.ts"; + const createFailure = Lint.Test.createFailuresOnFile(fileName, failureString); + const expectedFailures = [ + createFailure([1, 5], [1, 8]), + createFailure([2, 5], [2, 11]), + createFailure([3, 5], [3, 11]), + createFailure([4, 5], [4, 11]), + createFailure([5, 5], [5, 11]), + createFailure([6, 5], [6, 12]), + createFailure([7, 5], [7, 12]), + createFailure([8, 5], [8, 14]), + ]; + + const actualFailures = Lint.Test.applyRuleOnFile(fileName, VariableNameRule); + Lint.Test.assertFailuresEqual(actualFailures, expectedFailures); + }); + + it("checks declared let variables", () => { + const fileName = "rules/nokeywordnamedvariables_2.test.ts"; + const createFailure = Lint.Test.createFailuresOnFile(fileName, failureString); + const expectedFailures = [ + createFailure([1, 5], [1, 8]), + createFailure([2, 5], [2, 11]), + createFailure([3, 5], [3, 11]), + createFailure([4, 5], [4, 11]), + createFailure([5, 5], [5, 11]), + createFailure([6, 5], [6, 12]), + createFailure([7, 5], [7, 12]), + createFailure([8, 5], [8, 14]), + ]; + + const actualFailures = Lint.Test.applyRuleOnFile(fileName, VariableNameRule); + Lint.Test.assertFailuresEqual(actualFailures, expectedFailures); + }); + + it("checks parameters", () => { + const fileName = "rules/nokeywordnamedvariables_3.test.ts"; + const createFailure = Lint.Test.createFailuresOnFile(fileName, failureString); + const expectedFailures = [ + createFailure([1, 13], [1, 16]), + createFailure([2, 13], [2, 19]), + createFailure([3, 13], [3, 19]), + createFailure([4, 13], [4, 19]), + createFailure([5, 13], [5, 19]), + createFailure([6, 13], [6, 20]), + createFailure([7, 13], [7, 20]), + createFailure([8, 13], [8, 22]), + createFailure([10, 13], [10, 16]), + createFailure([11, 13], [11, 19]), + createFailure([12, 13], [12, 19]), + createFailure([13, 13], [13, 19]), + createFailure([14, 13], [14, 19]), + createFailure([15, 13], [15, 20]), + createFailure([16, 13], [16, 20]), + createFailure([17, 13], [17, 22]), + ]; + + const actualFailures = Lint.Test.applyRuleOnFile(fileName, VariableNameRule); + Lint.Test.assertFailuresEqual(actualFailures, expectedFailures); + }); + + it("checks destructuring assignment", () => { + const fileName = "rules/nokeywordnamedvariables_4.test.ts"; + const createFailure = Lint.Test.createFailuresOnFile(fileName, failureString); + const expectedFailures = [ + createFailure([1, 7], [1, 10]), + createFailure([2, 7], [2, 13]), + createFailure([3, 7], [3, 13]), + createFailure([4, 7], [4, 13]), + createFailure([5, 7], [5, 13]), + createFailure([6, 7], [6, 14]), + createFailure([7, 7], [7, 14]), + createFailure([8, 7], [8, 16]), + ]; + + const actualFailures = Lint.Test.applyRuleOnFile(fileName, VariableNameRule); + Lint.Test.assertFailuresEqual(actualFailures, expectedFailures); + }); + + it("checks destructuring variable declarations", () => { + const fileName = "rules/nokeywordnamedvariables_5.test.ts"; + const createFailure = Lint.Test.createFailuresOnFile(fileName, failureString); + const expectedFailures = [ + createFailure([1, 7], [1, 10]), + createFailure([2, 7], [2, 13]), + createFailure([3, 7], [3, 13]), + createFailure([4, 7], [4, 13]), + createFailure([5, 7], [5, 13]), + createFailure([6, 7], [6, 14]), + createFailure([7, 7], [7, 14]), + createFailure([8, 7], [8, 16]), + ]; + + const actualFailures = Lint.Test.applyRuleOnFile(fileName, VariableNameRule); + Lint.Test.assertFailuresEqual(actualFailures, expectedFailures); + }); +}); diff --git a/test/tsconfig.json b/test/tsconfig.json index 257e1693a31..28a62dcda31 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -44,6 +44,7 @@ "./rules/indentRuleTests.ts", "./rules/interfaceNameRuleTests.ts", "./rules/jsdocFormatRuleTests.ts", + "./rules/noKeywordNamedVariablesRuleTests.ts", "./rules/labelPositionRuleTests.ts", "./rules/labelUndefinedRuleTests.ts", "./rules/maxLineLengthRuleTests.ts",