From d2fcbf6433141933ea21d8d66d18d510195a558c Mon Sep 17 00:00:00 2001 From: ninevra Date: Mon, 7 Dec 2020 16:39:10 -0800 Subject: [PATCH 1/4] Test crash in no-statement-after-end Unreachable code in the global scope results in an undefined `currentSegmentInfo`, crashing no-statement-after-end. --- test/no-statement-after-end.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/no-statement-after-end.js b/test/no-statement-after-end.js index 2230803b..466d4533 100644 --- a/test/no-statement-after-end.js +++ b/test/no-statement-after-end.js @@ -30,7 +30,14 @@ 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; + ` ], invalid: [ { From 53f397f1c991070c9c93137a7109fa1b5a1d078b Mon Sep 17 00:00:00 2001 From: ninevra Date: Mon, 7 Dec 2020 17:25:49 -0800 Subject: [PATCH 2/4] Test missense in no-statement-after-end --- test/no-statement-after-end.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/no-statement-after-end.js b/test/no-statement-after-end.js index 466d4533..b5b821a5 100644 --- a/test/no-statement-after-end.js +++ b/test/no-statement-after-end.js @@ -37,7 +37,15 @@ ruleTester.run('no-statement-after-end', rule, { throw new Error(); 1; - ` + `, + cbTest(` + function newCodePath() { + throw new Error('make some unreachable code'); + t.end(); + } + + 1; + `) ], invalid: [ { From d1f9f2038c38d477601314da7e1565cf1d94edf1 Mon Sep 17 00:00:00 2001 From: ninevra Date: Mon, 7 Dec 2020 17:16:12 -0800 Subject: [PATCH 3/4] Push/pop segments only on path change --- rules/no-statement-after-end.js | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) 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 From 195051c2f7294cc131d04a7233be3cf45f3fb050 Mon Sep 17 00:00:00 2001 From: ninevra Date: Mon, 7 Dec 2020 20:01:53 -0800 Subject: [PATCH 4/4] Test that new paths don't hide the current segment --- test/no-statement-after-end.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/no-statement-after-end.js b/test/no-statement-after-end.js index b5b821a5..2aef50f5 100644 --- a/test/no-statement-after-end.js +++ b/test/no-statement-after-end.js @@ -63,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 } ] });