Skip to content

Commit

Permalink
Differentiate between break and continue inside switch statements
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed Oct 17, 2019
1 parent 34269b0 commit 7f01ea8
Show file tree
Hide file tree
Showing 14 changed files with 71 additions and 16 deletions.
2 changes: 2 additions & 0 deletions src/ast/ExecutionContext.ts
Expand Up @@ -3,6 +3,7 @@ import { PathTracker } from './utils/PathTracker';
import ThisVariable from './variables/ThisVariable';

interface ExecutionContextIgnore {
breakAndContinue: boolean;
breakStatements: boolean;
labels: Set<string>;
returnAwaitYield: boolean;
Expand Down Expand Up @@ -39,6 +40,7 @@ export function createHasEffectsContext(): HasEffectsContext {
breakFlow: BREAKFLOW_NONE,
called: new PathTracker(),
ignore: {
breakAndContinue: false,
breakStatements: false,
labels: new Set(),
returnAwaitYield: false
Expand Down
1 change: 1 addition & 0 deletions src/ast/nodes/ArrowFunctionExpression.ts
Expand Up @@ -58,6 +58,7 @@ export default class ArrowFunctionExpression extends NodeBase {
}
const { ignore, breakFlow } = context;
context.ignore = {
breakAndContinue: false,
breakStatements: false,
labels: new Set(),
returnAwaitYield: true
Expand Down
6 changes: 5 additions & 1 deletion src/ast/nodes/BreakStatement.ts
Expand Up @@ -8,7 +8,11 @@ export default class BreakStatement extends StatementBase {
type!: NodeType.tBreakStatement;

hasEffects(context: HasEffectsContext) {
if (!(this.label ? context.ignore.labels.has(this.label.name) : context.ignore.breakStatements))
if (
!(this.label
? context.ignore.labels.has(this.label.name)
: context.ignore.breakStatements || context.ignore.breakAndContinue)
)
return true;
context.breakFlow = new Set([this.label && this.label.name]);
return false;
Expand Down
4 changes: 3 additions & 1 deletion src/ast/nodes/ContinueStatement.ts
Expand Up @@ -8,7 +8,9 @@ export default class ContinueStatement extends StatementBase {
type!: NodeType.tContinueStatement;

hasEffects(context: HasEffectsContext) {
if (!(this.label ? context.ignore.labels.has(this.label.name) : context.ignore.breakStatements))
if (
!(this.label ? context.ignore.labels.has(this.label.name) : context.ignore.breakAndContinue)
)
return true;
context.breakFlow = new Set([this.label && this.label.name]);
return false;
Expand Down
6 changes: 3 additions & 3 deletions src/ast/nodes/DoWhileStatement.ts
Expand Up @@ -11,11 +11,11 @@ export default class DoWhileStatement extends StatementBase {
if (this.test.hasEffects(context)) return true;
const {
breakFlow,
ignore: { breakStatements }
ignore: { breakAndContinue }
} = context;
context.ignore.breakStatements = true;
context.ignore.breakAndContinue = true;
if (this.body.hasEffects(context)) return true;
context.ignore.breakStatements = breakStatements;
context.ignore.breakAndContinue = breakAndContinue;
if (context.breakFlow instanceof Set && context.breakFlow.has(null)) {
context.breakFlow = breakFlow;
}
Expand Down
6 changes: 3 additions & 3 deletions src/ast/nodes/ForInStatement.ts
Expand Up @@ -36,11 +36,11 @@ export default class ForInStatement extends StatementBase {
return true;
const {
breakFlow,
ignore: { breakStatements }
ignore: { breakAndContinue }
} = context;
context.ignore.breakStatements = true;
context.ignore.breakAndContinue = true;
if (this.body.hasEffects(context)) return true;
context.ignore.breakStatements = breakStatements;
context.ignore.breakAndContinue = breakAndContinue;
context.breakFlow = breakFlow;
return false;
}
Expand Down
6 changes: 3 additions & 3 deletions src/ast/nodes/ForStatement.ts
Expand Up @@ -27,11 +27,11 @@ export default class ForStatement extends StatementBase {
return true;
const {
breakFlow,
ignore: { breakStatements }
ignore: { breakAndContinue }
} = context;
context.ignore.breakStatements = true;
context.ignore.breakAndContinue = true;
if (this.body.hasEffects(context)) return true;
context.ignore.breakStatements = breakStatements;
context.ignore.breakAndContinue = breakAndContinue;
context.breakFlow = breakFlow;
return false;
}
Expand Down
6 changes: 3 additions & 3 deletions src/ast/nodes/WhileStatement.ts
Expand Up @@ -11,11 +11,11 @@ export default class WhileStatement extends StatementBase {
if (this.test.hasEffects(context)) return true;
const {
breakFlow,
ignore: { breakStatements }
ignore: { breakAndContinue }
} = context;
context.ignore.breakStatements = true;
context.ignore.breakAndContinue = true;
if (this.body.hasEffects(context)) return true;
context.ignore.breakStatements = breakStatements;
context.ignore.breakAndContinue = breakAndContinue;
context.breakFlow = breakFlow;
return false;
}
Expand Down
1 change: 1 addition & 0 deletions src/ast/nodes/shared/FunctionNode.ts
Expand Up @@ -74,6 +74,7 @@ export default class FunctionNode extends NodeBase {
);
const { breakFlow, ignore } = context;
context.ignore = {
breakAndContinue: false,
breakStatements: false,
labels: new Set(),
returnAwaitYield: true
Expand Down
3 changes: 1 addition & 2 deletions test/form/samples/side-effects-switch-statements/_config.js
@@ -1,4 +1,3 @@
module.exports = {
description: 'switch statements should be correctly tree-shaken',
options: { output: { name: 'myBundle' } }
description: 'switch statements should be correctly tree-shaken'
};
9 changes: 9 additions & 0 deletions test/form/samples/side-effects-switch-statements/_expected.js
Expand Up @@ -44,3 +44,12 @@ switch (globalThis.unknown) {
break;
case 'bar':
}

for (var i = 0; i < 4; i++) {
switch (i) {
case 0:
case 1:
continue;
}
effect();
}
9 changes: 9 additions & 0 deletions test/form/samples/side-effects-switch-statements/main.js
Expand Up @@ -65,3 +65,12 @@ switch (globalThis.unknown) {
case 'bar':
noEffect();
}

for (var i = 0; i < 4; i++) {
switch (i) {
case 0:
case 1:
continue;
}
effect();
}
@@ -0,0 +1,3 @@
module.exports = {
description: 'treats continue different from break in switch statements'
};
25 changes: 25 additions & 0 deletions test/function/samples/switch-statement-nested-continue/main.js
@@ -0,0 +1,25 @@
function getValueContinue() {
for (var i = 0; i < 4; i++) {
switch (i) {
case 0:
case 1:
continue;
}
return i;
}
}

assert.strictEqual(getValueContinue(), 2);

function getValueBreak() {
for (var i = 0; i < 4; i++) {
switch (i) {
case 0:
case 1:
break;
}
return i;
}
}

assert.strictEqual(getValueBreak(), 0);

0 comments on commit 7f01ea8

Please sign in to comment.