diff --git a/src/configs/latest.ts b/src/configs/latest.ts index 7cc43241c04..0f7889285bf 100644 --- a/src/configs/latest.ts +++ b/src/configs/latest.ts @@ -19,7 +19,6 @@ // tslint:disable:object-literal-key-quotes export const rules = { // added in v3.x - "no-invalid-this": false, "no-angle-bracket-type-assertion": true, // added in v4.1 diff --git a/src/configs/recommended.ts b/src/configs/recommended.ts index 324c84677e3..2d6c26b1a45 100644 --- a/src/configs/recommended.ts +++ b/src/configs/recommended.ts @@ -81,7 +81,6 @@ export const rules = { "no-reference": true, "no-shadowed-variable": true, "no-string-literal": true, - "no-switch-case-fall-through": false, "no-trailing-whitespace": true, "no-unsafe-finally": true, "no-unused-expression": true, @@ -126,7 +125,6 @@ export const rules = { "semicolon": { options: ["always"], }, - "switch-default": true, "trailing-comma": { options: { multiline: "always", @@ -213,7 +211,6 @@ export const jsRules = { "no-reference": true, "no-shadowed-variable": true, "no-string-literal": true, - "no-switch-case-fall-through": false, "no-trailing-whitespace": true, "no-unused-expression": true, // disable this rule as it is very heavy performance-wise and not that useful @@ -241,7 +238,6 @@ export const jsRules = { "semicolon": { options: ["always"], }, - "switch-default": true, "trailing-comma": { options: { multiline: "always", diff --git a/src/rules/noInferrableTypesRule.ts b/src/rules/noInferrableTypesRule.ts index 8cd14f6cf25..fc69663b365 100644 --- a/src/rules/noInferrableTypesRule.ts +++ b/src/rules/noInferrableTypesRule.ts @@ -89,7 +89,8 @@ class NoInferrableTypesWalker extends Lint.AbstractWalker { hasModifier(node.modifiers, ts.SyntaxKind.ReadonlyKeyword)) { break; } - /* falls through*/ + this.checkDeclaration(node as ts.VariableLikeDeclaration); + break; case ts.SyntaxKind.VariableDeclaration: this.checkDeclaration(node as ts.VariableLikeDeclaration); } diff --git a/src/rules/noInvalidThisRule.ts b/src/rules/noInvalidThisRule.ts deleted file mode 100644 index d5e3e37e629..00000000000 --- a/src/rules/noInvalidThisRule.ts +++ /dev/null @@ -1,98 +0,0 @@ -/** - * @license - * Copyright 2016 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 ts from "typescript"; - -import * as Lint from "../index"; - -const OPTION_FUNCTION_IN_METHOD = "check-function-in-method"; -const DEPRECATED_OPTION_FUNCTION_IN_METHOD = "no-this-in-function-in-method"; - -export class Rule extends Lint.Rules.AbstractRule { - /* tslint:disable:object-literal-sort-keys */ - public static metadata: Lint.IRuleMetadata = { - ruleName: "no-invalid-this", - description: "Disallows using the `this` keyword outside of classes.", - rationale: "See [the rule's author's rationale here.](https://github.com/palantir/tslint/pull/1105#issue-147549402)", - optionsDescription: Lint.Utils.dedent` - One argument may be optionally provided: - - * \`${OPTION_FUNCTION_IN_METHOD}\` disallows using the \`this\` keyword in functions within class methods.`, - options: { - type: "array", - items: { - type: "string", - enum: [OPTION_FUNCTION_IN_METHOD], - }, - minLength: 0, - maxLength: 1, - }, - optionExamples: ["true", `[true, "${OPTION_FUNCTION_IN_METHOD}"]`], - type: "functionality", - typescriptOnly: false, - }; - /* tslint:enable:object-literal-sort-keys */ - - public static FAILURE_STRING_OUTSIDE = "the \"this\" keyword is disallowed outside of a class body" ; - public static FAILURE_STRING_INSIDE = "the \"this\" keyword is disallowed in function bodies inside class methods, " + - "use arrow functions instead"; - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - const hasOption = (name: string) => this.ruleArguments.indexOf(name) !== -1; - const checkFuncInMethod = hasOption(DEPRECATED_OPTION_FUNCTION_IN_METHOD) || hasOption(OPTION_FUNCTION_IN_METHOD); - return this.applyWithFunction(sourceFile, walk, checkFuncInMethod); - } -} - -function walk(ctx: Lint.WalkContext): void { - const { sourceFile, options: checkFuncInMethod } = ctx; - let inClass = false; - let inFunctionInClass = false; - - ts.forEachChild(sourceFile, function cb(node: ts.Node) { - switch (node.kind) { - case ts.SyntaxKind.ClassDeclaration: - case ts.SyntaxKind.ClassExpression: - if (!inClass) { - inClass = true; - ts.forEachChild(node, cb); - inClass = false; - return; - } - break; - - case ts.SyntaxKind.FunctionDeclaration: - case ts.SyntaxKind.FunctionExpression: - if (inClass) { - inFunctionInClass = true; - ts.forEachChild(node, cb); - inFunctionInClass = false; - return; - } - break; - - case ts.SyntaxKind.ThisKeyword: - if (!inClass) { - ctx.addFailureAtNode(node, Rule.FAILURE_STRING_OUTSIDE); - } else if (checkFuncInMethod && inFunctionInClass) { - ctx.addFailureAtNode(node, Rule.FAILURE_STRING_INSIDE); - } - return; - } - - ts.forEachChild(node, cb); - }); -} diff --git a/src/rules/noSwitchCaseFallThroughRule.ts b/src/rules/noSwitchCaseFallThroughRule.ts deleted file mode 100644 index b93ff9f605a..00000000000 --- a/src/rules/noSwitchCaseFallThroughRule.ts +++ /dev/null @@ -1,118 +0,0 @@ -/** - * @license - * 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 utils from "tsutils"; -import * as ts from "typescript"; - -import * as Lint from "../index"; - -export class Rule extends Lint.Rules.AbstractRule { - /* tslint:disable:object-literal-sort-keys */ - public static metadata: Lint.IRuleMetadata = { - ruleName: "no-switch-case-fall-through", - description: "Disallows falling through case statements.", - descriptionDetails: Lint.Utils.dedent` - For example, the following is not allowed: - - \`\`\`ts - switch(foo) { - case 1: - someFunc(foo); - case 2: - someOtherFunc(foo); - } - \`\`\` - - However, fall through is allowed when case statements are consecutive or - a magic \`/* falls through */\` comment is present. The following is valid: - - \`\`\`ts - switch(foo) { - case 1: - someFunc(foo); - /* falls through */ - case 2: - case 3: - someOtherFunc(foo); - } - \`\`\``, - rationale: "Fall though in switch statements is often unintentional and a bug.", - optionsDescription: "Not configurable.", - options: null, - optionExamples: ["true"], - type: "functionality", - typescriptOnly: false, - }; - /* tslint:enable:object-literal-sort-keys */ - - public static FAILURE_STRING_PART = "expected a 'break' before "; - - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new NoSwitchCaseFallThroughWalker(sourceFile, this.ruleName, undefined)); - } -} - -export class NoSwitchCaseFallThroughWalker extends Lint.AbstractWalker { - public walk(sourceFile: ts.SourceFile) { - const cb = (node: ts.Node): void => { - if (node.kind === ts.SyntaxKind.SwitchStatement) { - this.visitSwitchStatement(node as ts.SwitchStatement); - } - return ts.forEachChild(node, cb); - }; - return ts.forEachChild(sourceFile, cb); - } - - private visitSwitchStatement(node: ts.SwitchStatement) { - const clauses = node.caseBlock.clauses; - const len = clauses.length - 1; // last clause doesn't need to be checked - for (let i = 0; i < len; ++i) { - if (clauses[i].statements.length !== 0 && - // TODO type assertion can be removed with typescript 2.2 - !utils.endsControlFlow(clauses[i] as ts.CaseClause) && - !this.isFallThroughAllowed(clauses[i])) { - - this.reportError(clauses[i + 1]); - } - } - } - - private isFallThroughAllowed(clause: ts.CaseOrDefaultClause) { - const sourceFileText = this.sourceFile.text; - const comments = ts.getLeadingCommentRanges(sourceFileText, clause.end); - if (comments === undefined) { - return false; - } - for (const comment of comments) { - let commentText: string; - if (comment.kind === ts.SyntaxKind.MultiLineCommentTrivia) { - commentText = sourceFileText.substring(comment.pos + 2, comment.end - 2); - } else { - commentText = sourceFileText.substring(comment.pos + 2, comment.end); - } - if (commentText.trim() === "falls through") { - return true; - } - } - return false; - } - - private reportError(clause: ts.CaseOrDefaultClause) { - const keyword = clause.kind === ts.SyntaxKind.CaseClause ? "case" : "default"; - this.addFailureAt(clause.getStart(this.sourceFile), keyword.length, `${Rule.FAILURE_STRING_PART}'${keyword}'`); - } -} diff --git a/src/rules/noUnnecessaryQualifierRule.ts b/src/rules/noUnnecessaryQualifierRule.ts index b3c6c8dc5ee..68b06bb7d4e 100644 --- a/src/rules/noUnnecessaryQualifierRule.ts +++ b/src/rules/noUnnecessaryQualifierRule.ts @@ -72,7 +72,8 @@ class Walker extends Lint.ProgramAwareRuleWalker { this.visitNamespaceAccess(node, expression, name); break; } - // falls through + super.visitNode(node); + break; default: super.visitNode(node); } diff --git a/src/rules/trailingCommaRule.ts b/src/rules/trailingCommaRule.ts index 64bb5dbddf9..fcf61782815 100644 --- a/src/rules/trailingCommaRule.ts +++ b/src/rules/trailingCommaRule.ts @@ -98,7 +98,8 @@ class TrailingCommaWalker extends Lint.AbstractWalker { if ((node as ts.NewExpression).arguments === undefined) { break; } - // falls through + this.checkList((node as ts.CallExpression | ts.NewExpression).arguments!, node.end); + break; case ts.SyntaxKind.CallExpression: this.checkList((node as ts.CallExpression | ts.NewExpression).arguments!, node.end); break; diff --git a/src/tsconfig.json b/src/tsconfig.json index b85f41cf5f2..749f5c00043 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "module": "commonjs", + "noFallthroughCasesInSwitch": true, "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true, diff --git a/test/rules/no-invalid-this/enabled/test.js.lint b/test/rules/no-invalid-this/enabled/test.js.lint deleted file mode 100644 index ea9d9abc13a..00000000000 --- a/test/rules/no-invalid-this/enabled/test.js.lint +++ /dev/null @@ -1,60 +0,0 @@ -function foo(x) { - console.log(this.x); - ~~~~ [the "this" keyword is disallowed outside of a class body] - - this.evilMethod(); - ~~~~ [the "this" keyword is disallowed outside of a class body] -} - -class AClass { - x; - - constructor() { - this.x = 2; - } - - aMethod() { - this.x = 5; - } - - bMethod() { - [3,4].forEach(function(nr){ - console.log(this.x === nr); - ~~~~ [the "this" keyword is disallowed in function bodies inside class methods, use arrow functions instead] - }); - } - - Method() { - [3,4].forEach((nr) => { - console.log(this.x === nr); - }); - } - - cMethod = () => { - [3,4].forEach(function(nr){ - console.log(this.x === nr); - ~~~~ [the "this" keyword is disallowed in function bodies inside class methods, use arrow functions instead] - }); - } - - dMethod() { - [3,4].forEach(badFunction); - function badFunction(nr) { - console.log(this.x === nr); - ~~~~ [the "this" keyword is disallowed in function bodies inside class methods, use arrow functions instead] - } - } - - eMethod() { - [3,4].forEach(badFunction); - let badFunction = nr => console.log(this.x === nr); - } -} - -const AClassExpression = class { - x; - - aMethod() { - this.x = 5; - } -} diff --git a/test/rules/no-invalid-this/enabled/test.ts.lint b/test/rules/no-invalid-this/enabled/test.ts.lint deleted file mode 100644 index da3be2bb639..00000000000 --- a/test/rules/no-invalid-this/enabled/test.ts.lint +++ /dev/null @@ -1,60 +0,0 @@ -function foo(x: number) { - console.log(this.x); - ~~~~ [the "this" keyword is disallowed outside of a class body] - - this.evilMethod(); - ~~~~ [the "this" keyword is disallowed outside of a class body] -} - -class AClass { - private x: number; - - constructor() { - this.x = 2; - } - - public aMethod() { - this.x = 5; - } - - public bMethod() { - [3,4].forEach(function(nr){ - console.log(this.x === nr); - ~~~~ [the "this" keyword is disallowed in function bodies inside class methods, use arrow functions instead] - }); - } - - public bMethod() { - [3,4].forEach((nr) => { - console.log(this.x === nr); - }); - } - - public cMethod = () => { - [3,4].forEach(function(nr){ - console.log(this.x === nr); - ~~~~ [the "this" keyword is disallowed in function bodies inside class methods, use arrow functions instead] - }); - } - - public dMethod() { - [3,4].forEach(badFunction); - function badFunction(nr) { - console.log(this.x === nr); - ~~~~ [the "this" keyword is disallowed in function bodies inside class methods, use arrow functions instead] - } - } - - public eMethod() { - [3,4].forEach(badFunction); - let badFunction = nr => console.log(this.x === nr); - } -} - -const AClassExpression = class { - private x: number; - - public aMethod() { - this.x = 5; - } -} diff --git a/test/rules/no-invalid-this/enabled/tslint.json b/test/rules/no-invalid-this/enabled/tslint.json deleted file mode 100644 index 9ecceb14846..00000000000 --- a/test/rules/no-invalid-this/enabled/tslint.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "rules": { - "no-invalid-this": [ - true, - "check-function-in-method" - ] - }, - "jsRules": { - "no-invalid-this": [ - true, - "check-function-in-method" - ] - } -} diff --git a/test/rules/no-switch-case-fall-through/test.js.lint b/test/rules/no-switch-case-fall-through/test.js.lint deleted file mode 100644 index 0c53e2a05b0..00000000000 --- a/test/rules/no-switch-case-fall-through/test.js.lint +++ /dev/null @@ -1,84 +0,0 @@ -switch (foo) { - case 1: - bar(); - case 2: - ~~~~ [expected a 'break' before 'case'] - bar(); - bar(); - case 3: - ~~~~ [expected a 'break' before 'case'] - case 4: - default: - break; -} - - -switch (foo) { - case 1: - bar(); - case 2: - ~~~~ [expected a 'break' before 'case'] - bar(); -} - - -switch (foo) { - case 1: - case 2: - default: - bar(); -} - -switch (foo) { - case 1: - switch (bar) { - case "": - default: - break; - } - case 2: - ~~~~ [expected a 'break' before 'case'] -} - -switch (foo) { - case 1: - case 2: - - case 3: - - case 4: - break; - default: - bar(); -} - - -switch (foo) { - case 1: - return; // handle return the same as break - case 2: - case 3: - throw "error"; -} - -switch (foo) { - case 1: - bar(); - /* falls through */ - case 2: - bar(); - /* Testing */ - /* falls through */ - case 3: - break; -} - -// valid -switch (foo) { - case 1: - break; - case 2: - case 3: - break; - default: -} diff --git a/test/rules/no-switch-case-fall-through/test.ts.lint b/test/rules/no-switch-case-fall-through/test.ts.lint deleted file mode 100644 index 9a37931a68e..00000000000 --- a/test/rules/no-switch-case-fall-through/test.ts.lint +++ /dev/null @@ -1,131 +0,0 @@ -switch (foo) { - case 1: - bar(); - case 2: - ~~~~ [expected a 'break' before 'case'] - bar(); - bar(); - case 3: - ~~~~ [expected a 'break' before 'case'] - case 4: - default: - break; -} - - -switch (foo) { - case 1: - bar(); - case 2: - ~~~~ [expected a 'break' before 'case'] - bar(); -} - - -switch (foo) { - case 1: - case 2: - default: - bar(); -} - -switch (foo) { - case 1: - switch (bar) { - case "": - default: - break; - } - case 2: - ~~~~ [expected a 'break' before 'case'] -} - -switch (foo) { - case 1: - case 2: - - case 3: - - case 4: - break; - default: - bar(); -} - - -switch (foo) { - case 1: - return; // handle return the same as break - case 2: - case 3: - throw "error"; -} - -switch (foo) { - case 1: - bar(); - /* falls through */ - case 2: - bar(); - /* Testing */ - /* falls through */ - default: - bar(); - // falls through - case 3: - break; -} - -// valid -switch (foo) { - case 1: - break; - case 2: - case 3: - break; - default: -} - -// recognise blocks -switch (foo) { - case 1: { - break; - } - case 2: -} - -// recognise if-else -switch (foo) { - case 1: - if (bar) { - break; - } else { - return; - } - case 2: - if (bar) { - break; - } - case 3: - ~~~~ [expected a 'break' before 'case'] -} - -// allow throw -switch (foo) { - case 1: - throw bar; - case 2: -} - -// handle nested switch statements -switch (foo) { - case 1: - switch (bar) { - case 1: - foobar(); // this case clause falls through and returns in the default clause - default: - ~~~~~~~ [expected a 'break' before 'default'] - return; - } - case 2: -} \ No newline at end of file diff --git a/test/rules/no-switch-case-fall-through/tslint.json b/test/rules/no-switch-case-fall-through/tslint.json deleted file mode 100644 index 25ad95f50a3..00000000000 --- a/test/rules/no-switch-case-fall-through/tslint.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "rules": { - "no-switch-case-fall-through": true - }, - "jsRules": { - "no-switch-case-fall-through": true - } -} diff --git a/test/tsconfig.json b/test/tsconfig.json index cdc452339dc..7ba45b600b3 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "module": "commonjs", + "noFallthroughCasesInSwitch": true, "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true, diff --git a/tslint.json b/tslint.json index 7e14acb110b..112ddb8e22f 100644 --- a/tslint.json +++ b/tslint.json @@ -26,7 +26,6 @@ "no-default-export": true, "no-empty-interface": true, "no-string-throw": true, - "no-switch-case-fall-through": true, "prefer-const": true, "switch-default": false, "variable-name": [true,