diff --git a/rules/no-statement-after-end.js b/rules/no-statement-after-end.js index 90ef1261..58678cc0 100644 --- a/rules/no-statement-after-end.js +++ b/rules/no-statement-after-end.js @@ -21,10 +21,19 @@ const create = context => { let currentSegmentInfo; + function pathStart() { + if (currentSegmentInfo !== undefined) { + segmentInfoStack.push(currentSegmentInfo); + currentSegmentInfo = undefined; + } + } + + function pathEnd() { + currentSegmentInfo = segmentInfoStack.pop(); + } + function segmentStart(segment) { // A new CodePathSegment has started, create an "info" object to track this segments state. - segmentInfoStack.push(currentSegmentInfo); - currentSegmentInfo = { ended: false, prev: segment.prevSegments.map(previousSegment => segmentInfoMap.get(previousSegment.id)) @@ -34,12 +43,14 @@ const create = context => { } function segmentEnd() { - currentSegmentInfo = segmentInfoStack.pop(); + currentSegmentInfo = undefined; } function checkForEndExpression(node) { if (isEndExpression(node)) { - currentSegmentInfo.ended = true; + if (currentSegmentInfo !== undefined) { + currentSegmentInfo.ended = true; + } } } @@ -48,6 +59,12 @@ const create = context => { return; } + // If there is no current segment (this occurs in unreachable code), then we + // can't check whether `t.end()` was called + if (currentSegmentInfo === undefined) { + return; + } + const ended = [currentSegmentInfo] .concat(currentSegmentInfo.prev) .filter(info => info.ended); @@ -85,6 +102,8 @@ const create = context => { checkStatement(node); } }, + onCodePathStart: pathStart, + onCodePathEnd: pathEnd, onCodePathSegmentStart: segmentStart, onCodePathSegmentEnd: segmentEnd, CallExpression: checkForEndExpression diff --git a/test/no-statement-after-end.js b/test/no-statement-after-end.js index 2230803b..2aef50f5 100644 --- a/test/no-statement-after-end.js +++ b/test/no-statement-after-end.js @@ -30,7 +30,22 @@ ruleTester.run('no-statement-after-end', rule, { cbTest('return t.end();'), cbTest('t.end(); return;'), // Valid because it is not a test file (no header) - cbTest('t.end(); t.is(1, 1);', false) + cbTest('t.end(); t.is(1, 1);', false), + ` + const test = require('ava'); + + throw new Error(); + + 1; + `, + cbTest(` + function newCodePath() { + throw new Error('make some unreachable code'); + t.end(); + } + + 1; + `) ], invalid: [ { @@ -48,6 +63,20 @@ ruleTester.run('no-statement-after-end', rule, { { code: cbTest('if (t.context.a === 1) { t.end(); }\nt.is(1, 1); t.end();'), errors + }, + { + code: cbTest(` + function newCodePath() { + // ... + } + t.end(); + 1; + `), + errors + }, + { + code: cbTest('t.end(); function newCodePath() {} 1;'), + errors } ] });