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

Commit

Permalink
Merge pull request #164 from DickvdBrink/switchCaseFallThrough
Browse files Browse the repository at this point in the history
Switch case fall through
  • Loading branch information
ashwinr committed Jun 23, 2014
2 parents 78ae217 + cd5766b commit 66a77f9
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ A sample configuration file with all options is available [here](https://github.
* `no-empty` disallows empty blocks.
* `no-eval` disallows `eval` function invocations.
* `no-string-literal` disallows object access via string literals.
* `no-switch-case-fall-through` disallows falling through case statements.
* `no-trailing-comma` disallows trailing comma within object literals.
* `no-trailing-whitespace` disallows trailing whitespace at the end of a line.
* `no-unreachable` disallows unreachable code after `break`, `catch`, `throw`, and `return` statements.
Expand Down
73 changes: 73 additions & 0 deletions src/rules/noSwitchCaseFallThroughRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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.
*/

/// <reference path='../../lib/tslint.d.ts' />

export class Rule extends Lint.Rules.AbstractRule {
public static FAILURE_STRING_PART = "Expected a 'break' before ";

public apply(syntaxTree: TypeScript.SyntaxTree): Lint.RuleFailure[] {
return this.applyWithWalker(new NoSwitchCaseFallThroughWalker(syntaxTree, this.getOptions()));
}
}

export class NoSwitchCaseFallThroughWalker extends Lint.RuleWalker {

public visitSwitchStatement(node: TypeScript.SwitchStatementSyntax) {
var isFallingThrough = false;
// get position for first case statement
var position = this.positionAfter(node.switchKeyword, node.openParenToken, node.expression,
node.closeParenToken, node.openBraceToken);
var switchClauses = node.switchClauses;
for (var i = 0; i < switchClauses.childCount(); i++) {
var child = switchClauses.childAt(i);
var kind = child.kind();
var fullWidth = child.fullWidth();
if (kind === TypeScript.SyntaxKind.CaseSwitchClause) {
position += fullWidth;
var switchClause = <TypeScript.CaseSwitchClauseSyntax>child;
isFallingThrough = this.fallsThrough(switchClause.statements);
// no break statements and no statements means the fallthrough is expected.
// last item doesn't need a break
if (isFallingThrough && switchClause.statements.childCount() > 0 && ((switchClauses.childCount() - 1) > i)) {
// remove trailing trivia (new line)
this.addFailure(this.createFailure(position - child.trailingTriviaWidth(), 1, Rule.FAILURE_STRING_PART + "'case'"));
}
} else {
// case statement falling through a default, this is always an error
if (isFallingThrough) {
// remove trailing trivia (new line)
this.addFailure(this.createFailure(position - child.trailingTriviaWidth(), 1, Rule.FAILURE_STRING_PART + "'default'"));
}
// add the width after setting the failure, the error isn't at the end of the default but right before it.
position += fullWidth;
}
}
super.visitSwitchStatement(node);
}

private fallsThrough(list: TypeScript.ISyntaxList) {
for (var i = 0; i < list.childCount(); i++) {
var nodeKind = list.childAt(i).kind();
if (nodeKind === TypeScript.SyntaxKind.BreakStatement ||
nodeKind === TypeScript.SyntaxKind.ThrowStatement ||
nodeKind === TypeScript.SyntaxKind.ReturnStatement) {
return false;
}
}
return true;
}
}
68 changes: 68 additions & 0 deletions test/files/rules/noswitchcasefallthrough.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
switch (foo) {
case 1:
bar();
case 2:
bar();
bar();
case 3:
case 4:
default:
break;
}


switch (foo) {
case 1:
bar();
case 2:
bar();
}


switch (foo) {
case 1:
case 2:
default:
bar();
}

switch (foo) {
case 1:
switch (bar) {
case "":
default:
break;
}
case 2:
}

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";
}

// valid
switch (foo) {
case 1:
break;
case 2:
case 3:
break;
default:
}
41 changes: 41 additions & 0 deletions test/rules/noSwitchCaseFallThroughRuleTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.
*/

/// <reference path='../references.ts' />

describe("<no-switch-case-fall-through>", () => {
it("Switch fall through", () => {
var fileName = "rules/noswitchcasefallthrough.test.ts";
var Rule = Lint.Test.getRule("no-switch-case-fall-through");
var failureString = Rule.FAILURE_STRING_PART;
var failureDefault = Lint.Test.createFailuresOnFile(fileName, failureString + "'default'");
var failureCase = Lint.Test.createFailuresOnFile(fileName, failureString + "'case'");
var expectedFailures = [
failureCase([3, 15], [3, 16]),
failureCase([6, 15], [6, 16]),
failureDefault([8, 12], [8, 13]),
failureCase([16, 15], [16, 16]),
failureDefault([24, 12], [24, 13]),
failureCase([35, 10], [35, 11]),
failureDefault([32, 21], [32, 22])
];
var actualFailures = Lint.Test.applyRuleOnFile(fileName, Rule);

assert.lengthOf(actualFailures, 7);

Lint.Test.assertFailuresEqual(actualFailures, expectedFailures);
});
});
1 change: 1 addition & 0 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"no-empty": true,
"no-eval": true,
"no-string-literal": true,
"no-switch-case-fall-through": true,
"no-trailing-comma": true,
"no-trailing-whitespace": true,
"no-unused-expression": true,
Expand Down

0 comments on commit 66a77f9

Please sign in to comment.