diff --git a/src/ast/nodes/Literal.ts b/src/ast/nodes/Literal.ts index 5259ada5ef2..97bab48e2bf 100644 --- a/src/ast/nodes/Literal.ts +++ b/src/ast/nodes/Literal.ts @@ -70,6 +70,13 @@ export default class Literal extends Node return true; } case INTERACTION_CALLED: { + if ( + this.included && + this.value instanceof RegExp && + (this.value.global || this.value.sticky) + ) { + return true; + } return ( path.length !== 1 || hasMemberEffectWhenCalled(this.members, path[0], interaction, context) diff --git a/src/ast/values.ts b/src/ast/values.ts index 4d91b067f99..3a479a29136 100644 --- a/src/ast/values.ts +++ b/src/ast/values.ts @@ -200,6 +200,11 @@ const literalNumberMembers: MemberDescriptions = assembleMemberDescriptions( objectMembers ); +/** + * RegExp are stateful when they have the global or sticky flags set. + * But if we actually don't use them, the side effect does not matter. + * the check logic in `hasEffectsOnInteractionAtPath`. + */ const literalRegExpMembers: MemberDescriptions = assembleMemberDescriptions( { exec: returnsUnknown, diff --git a/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_config.js b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_config.js new file mode 100644 index 00000000000..6001c39d55d --- /dev/null +++ b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'check tree-shake on RegExp if it is used' +}; diff --git a/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_expected.js b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_expected.js new file mode 100644 index 00000000000..72a94a069d1 --- /dev/null +++ b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_expected.js @@ -0,0 +1,23 @@ +const commonRegexp_1 = /1/; +const commonRegexp_2 = /1/; + +assert.strictEqual(commonRegexp_1.test('1'), true); +assert.ok(commonRegexp_2.exec('1')); + +const globalRegexp_1 = /1/g; +const globalRegexp_2 = /1/g; + +globalRegexp_1.test('1'); +globalRegexp_2.exec('1'); + +assert.strictEqual(globalRegexp_1.test('1'), null); +assert.strictEqual(globalRegexp_2.exec('1'), null); + +const stickyRegexp_1 = /1/y; +const stickyRegexp_2 = /1/y; + +stickyRegexp_1.test('1'); +stickyRegexp_2.exec('1'); + +assert.strictEqual(stickyRegexp_1.test('1'), null); +assert.strictEqual(stickyRegexp_2.exec('1'), null); diff --git a/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/main.js b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/main.js new file mode 100644 index 00000000000..4124af7072d --- /dev/null +++ b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/main.js @@ -0,0 +1,26 @@ +const commonRegexp_1 = /1/; +const commonRegexp_2 = /1/; + +commonRegexp_1.test('1'); +commonRegexp_2.exec('1'); + +assert.strictEqual(commonRegexp_1.test('1'), true); +assert.ok(commonRegexp_2.exec('1')); + +const globalRegexp_1 = /1/g; +const globalRegexp_2 = /1/g; + +globalRegexp_1.test('1'); +globalRegexp_2.exec('1'); + +assert.strictEqual(globalRegexp_1.test('1'), null); +assert.strictEqual(globalRegexp_2.exec('1'), null); + +const stickyRegexp_1 = /1/y; +const stickyRegexp_2 = /1/y; + +stickyRegexp_1.test('1'); +stickyRegexp_2.exec('1'); + +assert.strictEqual(stickyRegexp_1.test('1'), null); +assert.strictEqual(stickyRegexp_2.exec('1'), null); diff --git a/test/form/samples/builtin-prototypes/literal/main.js b/test/form/samples/builtin-prototypes/literal/main.js index e079316f4d7..cb99661a2ed 100644 --- a/test/form/samples/builtin-prototypes/literal/main.js +++ b/test/form/samples/builtin-prototypes/literal/main.js @@ -49,8 +49,12 @@ const _numberIsPrototypeOf = (1).isPrototypeOf(1).valueOf(); const _numberPropertyIsEnumerable = (1).propertyIsEnumerable('toString').valueOf(); // RegExp prototype -/1/.test(2); -/1/.exec(1); +/1/.test('1'); +/1/.exec('1'); +/1/y.test('1'); +/1/y.exec('1'); +/1/g.test('1'); +/1/g.exec('1'); // string prototype 'ab'.valueOf();