diff --git a/src/ast/nodes/BinaryExpression.ts b/src/ast/nodes/BinaryExpression.ts index d1167831f79..5e73aa8d715 100644 --- a/src/ast/nodes/BinaryExpression.ts +++ b/src/ast/nodes/BinaryExpression.ts @@ -16,8 +16,32 @@ import type * as NodeType from './NodeType'; import { type LiteralValueOrUnknown, UnknownValue } from './shared/Expression'; import { type ExpressionNode, NodeBase } from './shared/Node'; +type Operator = + | '!=' + | '!==' + | '%' + | '&' + | '*' + | '**' + | '+' + | '-' + | '/' + | '<' + | '<<' + | '<=' + | '==' + | '===' + | '>' + | '>=' + | '>>' + | '>>>' + | '^' + | '|' + | 'in' + | 'instanceof'; + const binaryOperators: { - [operator: string]: (left: LiteralValue, right: LiteralValue) => LiteralValueOrUnknown; + [operator in Operator]?: (left: LiteralValue, right: LiteralValue) => LiteralValueOrUnknown; } = { '!=': (left, right) => left != right, '!==': (left, right) => left !== right, @@ -29,15 +53,13 @@ const binaryOperators: { '+': (left: any, right: any) => left + right, '-': (left: any, right: any) => left - right, '/': (left: any, right: any) => left / right, - '<': (left, right) => (left as NonNullable) < (right as NonNullable), + '<': (left, right) => left! < right!, '<<': (left: any, right: any) => left << right, - '<=': (left, right) => - (left as NonNullable) <= (right as NonNullable), + '<=': (left, right) => left! <= right!, '==': (left, right) => left == right, '===': (left, right) => left === right, - '>': (left, right) => (left as NonNullable) > (right as NonNullable), - '>=': (left, right) => - (left as NonNullable) >= (right as NonNullable), + '>': (left, right) => left! > right!, + '>=': (left, right) => left! >= right!, '>>': (left: any, right: any) => left >> right, '>>>': (left: any, right: any) => left >>> right, '^': (left: any, right: any) => left ^ right, @@ -79,8 +101,9 @@ export default class BinaryExpression extends NodeBase implements DeoptimizableE this.operator === '+' && this.parent instanceof ExpressionStatement && this.left.getLiteralValueAtPath(EMPTY_PATH, SHARED_RECURSION_TRACKER, this) === '' - ) + ) { return true; + } return super.hasEffects(context); } diff --git a/src/ast/nodes/NewExpression.ts b/src/ast/nodes/NewExpression.ts index e12915475e5..974a357bc2c 100644 --- a/src/ast/nodes/NewExpression.ts +++ b/src/ast/nodes/NewExpression.ts @@ -28,8 +28,9 @@ export default class NewExpression extends NodeBase { if ( (this.context.options.treeshake as NormalizedTreeshakingOptions).annotations && this.annotations - ) + ) { return false; + } return ( this.callee.hasEffects(context) || this.callee.hasEffectsOnInteractionAtPath(EMPTY_PATH, this.interaction, context) diff --git a/test/function/samples/instanceof/left-hand-effect/_config.js b/test/function/samples/instanceof/left-hand-effect/_config.js new file mode 100644 index 00000000000..edadb884deb --- /dev/null +++ b/test/function/samples/instanceof/left-hand-effect/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'retains side effects in the left hand side of instanceof' +}; diff --git a/test/function/samples/instanceof/left-hand-effect/main.js b/test/function/samples/instanceof/left-hand-effect/main.js new file mode 100644 index 00000000000..f29fbed86a7 --- /dev/null +++ b/test/function/samples/instanceof/left-hand-effect/main.js @@ -0,0 +1,14 @@ +let effect = false; + +class Bar {} +class Foo { + constructor() { + effect = true; + } +} + +if (new Foo() instanceof Bar) { + assert.fail('Wrong instance relation'); +} + +assert.ok(effect); diff --git a/test/function/samples/instanceof/right-hand-effect/_config.js b/test/function/samples/instanceof/right-hand-effect/_config.js new file mode 100644 index 00000000000..466922bcd67 --- /dev/null +++ b/test/function/samples/instanceof/right-hand-effect/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'retains side effects in the right hand side of instanceof' +}; diff --git a/test/function/samples/instanceof/right-hand-effect/main.js b/test/function/samples/instanceof/right-hand-effect/main.js new file mode 100644 index 00000000000..01fa8698a53 --- /dev/null +++ b/test/function/samples/instanceof/right-hand-effect/main.js @@ -0,0 +1,14 @@ +let effect = false; + +class Foo {} + +if ( + new Foo() instanceof + class { + [(effect = true)]() {} + } +) { + assert.fail('Wrong instance relation'); +} + +assert.ok(effect); diff --git a/test/function/samples/instanceof/used-parameter/_config.js b/test/function/samples/instanceof/used-parameter/_config.js new file mode 100644 index 00000000000..1312a923985 --- /dev/null +++ b/test/function/samples/instanceof/used-parameter/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'retains instanceof for function parameters' +}; diff --git a/test/function/samples/instanceof/used-parameter/main.js b/test/function/samples/instanceof/used-parameter/main.js new file mode 100644 index 00000000000..61c8724c78d --- /dev/null +++ b/test/function/samples/instanceof/used-parameter/main.js @@ -0,0 +1,12 @@ +let effect = false; + +class Foo {} + +function checkInstance(instance) { + if (instance instanceof Foo) { + effect = true; + } +} + +checkInstance(new Foo()); +assert.ok(effect); diff --git a/test/function/samples/instanceof/used/_config.js b/test/function/samples/instanceof/used/_config.js new file mode 100644 index 00000000000..fe5faa35411 --- /dev/null +++ b/test/function/samples/instanceof/used/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'retains instanceof if it is true' +}; diff --git a/test/function/samples/instanceof/used/main.js b/test/function/samples/instanceof/used/main.js new file mode 100644 index 00000000000..cbd5feb2583 --- /dev/null +++ b/test/function/samples/instanceof/used/main.js @@ -0,0 +1,5 @@ +class Foo {} + +if (!(new Foo() instanceof Foo)) { + assert.fail('instanceof not resolved correctly'); +}