Skip to content

Commit

Permalink
add tests for control flow analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
ajafff committed Mar 3, 2018
1 parent 685fa1d commit b39884f
Show file tree
Hide file tree
Showing 8 changed files with 459 additions and 3 deletions.
96 changes: 96 additions & 0 deletions test/rules/control-flow/if.ts.lint
@@ -0,0 +1,96 @@
export function test() {
~ [0]
const condition = true;

if (condition)
return 'foo';
~~~~~~ [0]

if (condition) {
~ [0]
throw 'foo';
~~~~~ [0]
}

if (condition) {
~ [0]
throw 'foo';
~~~~~ [0]
} else {
console.log('foo');
}

if (!condition) {
~ [0]
throw 'foo';
~~~~~ [0]
} else {
console.log('foo');
}

if (!condition) {
console.log('foo');
} else {
~ [0]
throw 'foo';
~~~~~ [0]
}

if (!condition) {
~ [0]
throw 'foo';
~~~~~ [0]
}

if (condition) {
~~ [0]
~[0]
throw 'foo'
~~~~~ [0]
} else {
~ [0]
return 'bar';
~~~~~~ [0]
}

if (true) {
~~ [0]
~ [0]
throw 'foo';
~~~~~ [0]
}

if (true) {
~~ [0]
~ [0]
throw 'foo';
~~~~~ [0]
} else {
console.log('foo');
}

if (false) {
~ [0]
throw 'foo';
~~~~~ [0]
} else {
console.log('foo');
}

if (false) {
~~ [0]
console.log('foo');
} else {
~ [0]
throw 'foo';
~~~~~ [0]
}

if (false) {
~ [0]
throw 'foo';
~~~~~ [0]
}
}

[0]: control flow end
89 changes: 89 additions & 0 deletions test/rules/control-flow/iteration.ts.lint
@@ -0,0 +1,89 @@
export function test() {
~ [0]
const condition = true;

for (;;)
~~~ [0]
return;
~~~~~~ [0]

outer: while (true) {
~ [0]
for (const e of [])
return;
~~~~~~ [0]
break;
~~~~~ [0]
}

outer: while (true) {
~~~~~ [0]
~~~~~ [0]
~ [0]
return;
~~~~~~ [0]
}

for (const key in {}) {
~ [0]
return;
~~~~~~ [0]
}

outer: while (condition) {
~ [0]
do {
~~ [0]
~ [0]
continue outer;
~~~~~~~~ [0]
} while (false)
}

outer: while (true) {
~~~~~ [0]
~ [0]
do {
~~ [0]
~ [0]
continue outer;
~~~~~~~~ [0]
} while (false)
}

outer: while (false) {
~ [0]
return;
~~~~~~ [0]
}

outer: for (; true; ) {
inner: while (true) {
~~~~~ [0]
~ [0]
if (condition)
break outer;
~~~~~ [0]
break inner;
~~~~~ [0]
}
}

outer: for (; true; ) {
~~~ [0]
~ [0]
inner: while (true) {
~~~~~ [0]
~~~~~ [0]
~ [0]
if (true)
~~ [0]
break outer;
~~~~~ [0]
break inner;
~~~~~ [0]
}
}
}

[0]: control flow end
108 changes: 108 additions & 0 deletions test/rules/control-flow/switch.ts.lint
@@ -0,0 +1,108 @@
function test() {
~ [0]
switch (Boolean()) {
~~~~~~ [0]
case true:
~~~~ [0]
return true;
~~~~~~ [0]
case false:
~~~~ [0]
return false;
~~~~~~ [0]
default:
~~~~~~~ [0]
return;
~~~~~~ [0]
}

switch (Boolean()) {
~~~~~~ [0]
case true:
~~~~ [0]
return true;
~~~~~~ [0]
case false:
console.log('foo');
default:
~~~~~~~ [0]
return false;
~~~~~~ [0]
}

switch (Boolean()) {
case true:
~~~~ [0]
return true;
~~~~~~ [0]
case false:
~~~~ [0]
return false;
~~~~~~ [0]
}

switch (Boolean()) {
default:
~~~~~~~ [0]
break;
~~~~~ [0]
}

for (;;) {
~ [0]
switch (Boolean()) {
~~~~~~ [0]
default:
~~~~~~~ [0]
continue;
~~~~~~~~ [0]
}
}

switch (Boolean()) {
case true:
~~~~ [0]
return true;
~~~~~~ [0]
}

switch (Boolean()) {
case true:
~~~~ [0]
return true;
~~~~~~ [0]
case false:
console.log('foo');
}
switch (Boolean()) {
case true:
~~~~ [0]
return true;
~~~~~~ [0]
default:
console.log('foo');
}

switch (Boolean()) {
~~~~~~ [0]
case true:
console.log('foo');
default:
~~~~~~~ [0]
return;
~~~~~~ [0]
}

switch (Boolean()) {
case true:
~~~~ [0]
break;
~~~~~ [0]
default:
~~~~~~~ [0]
return;
~~~~~~ [0]
}
}

[0]: control flow end
34 changes: 34 additions & 0 deletions test/rules/control-flow/testCfaRule.ts
@@ -0,0 +1,34 @@
import * as ts from 'typescript';
import * as Lint from 'tslint';
import { endsControlFlow } from '../../../util/control-flow';
import { convertAst } from '../../../util/convert-ast';
import { isBlockLike, isIterationStatement, isWithStatement, isIfStatement, isLabeledStatement } from '../../../typeguard/node';

export class Rule extends Lint.Rules.AbstractRule {
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk);
}
}

function walk(ctx: Lint.WalkContext<void>) {
const seen = new Set<ts.Node>();
for (const node of convertAst(ctx.sourceFile).flat) {
if (isBlockLike(node)) {
checkStatement(node);
node.statements.forEach(checkStatement);
} else if (isIterationStatement(node) || isWithStatement(node) || isLabeledStatement(node)) {
checkStatement(node.statement);
} else if (isIfStatement(node)) {
checkStatement(node.thenStatement);
if (node.elseStatement)
checkStatement(node.elseStatement);
}
}
function checkStatement(node: ts.Statement | ts.BlockLike) {
if (seen.has(node))
return;
seen.add(node);
if (endsControlFlow(node))
ctx.addFailureAtNode(node.getFirstToken(ctx.sourceFile), 'control flow end');
}
}

0 comments on commit b39884f

Please sign in to comment.