Skip to content

fix: eliminate unused statements in certain scenarios#19536

Merged
alexander-akait merged 2 commits intowebpack:mainfrom
hai-x:feat-dead-statement
May 20, 2025
Merged

fix: eliminate unused statements in certain scenarios#19536
alexander-akait merged 2 commits intowebpack:mainfrom
hai-x:feat-dead-statement

Conversation

@hai-x
Copy link
Member

@hai-x hai-x commented May 15, 2025

What kind of change does this PR introduce?

We have previously supported the elimination of unreachable (dead) branches in if statements in #6273.

In this PR, we adopt the same approach to remove some dead statements when tracked completely return/throw statement, as implemented in #14357.

And it also fixes #19514.

Did you add tests for your changes?

Yes

Does this PR introduce a breaking change?

No

What needs to be documented once your changes are merged?

No

return bool;
}
});
parser.hooks.deadStatement.tap(PLUGIN_NAME, statement => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about unusedStatement? dead is more a harsher word 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's fine for me. :)

const oldTerminated = parser.scope.terminated;
parser.scope.terminated = undefined;
parser.walkStatement(successExpression.fn.body);
parser.scope.terminated = oldTerminated;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we need here?

Copy link
Member Author

@hai-x hai-x May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We walk successExpressionArg and then walk errorExpressionArg in

In this test case

it("should not call error callback on exception thrown in require callback", function(done) {
it will throw in successExpressionArg and parsing scope will be terminated which leads to failed.

To keep it short and to the point, show the input and output.

input:

it("should call error callback on exception thrown in loading module", function(done) {
	require.ensure(['./throwing'], function(){
		require('./throwing');
	}, function(error) {
		expect(error).toBeInstanceOf(Error);
		expect(error.message).toBe('message');
		done();
	});
});

eliminated output

it("should call error callback on exception thrown in loading module", function(done) {
	require.ensure(['./throwing'], function(){
		require('./throwing');
	}, function(error) {
		expect(error).toBeInstanceOf(Error);
		{}
		{}
	});
});

I think it's very very rare user case and i also add some test for it.

const replacement =
declarations.length > 0 ? `{ var ${declarations.join(", ")}; }` : "{}";
const dep = new ConstDependency(
replacement,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a comment // removed by dead control flow\n${replacement} (maybe better) for debug reasons

@codspeed-hq
Copy link

codspeed-hq bot commented May 15, 2025

CodSpeed Performance Report

Merging #19536 will degrade performances by 81.71%

Comparing hai-x:feat-dead-statement (12b78a9) with main (843f788)

Summary

⚡ 80 improvements
❌ 5 regressions
✅ 48 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Benchmarks breakdown

Benchmark BASE HEAD Change
benchmark "devtool-eval", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 35.2 ms 39.2 ms -10.22%
benchmark "devtool-source-map", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 11.5 ms 44.7 ms -74.36%
benchmark "future-defaults", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 48.9 ms 44.1 ms +10.7%
benchmark "lodash", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 11.3 ms 30.8 ms -63.4%
benchmark "many-chunks-commonjs", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 11.2 ms 61.4 ms -81.71%
benchmark "many-chunks-esm", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 11.3 ms 52.3 ms -78.3%
md4 buffer benchmark (size: 10000) 114.1 µs 73.2 µs +55.78%
md4 buffer benchmark (size: 120) 75.9 µs 35.1 µs ×2.2
md4 buffer benchmark (size: 160) 76.1 µs 35.2 µs ×2.2
md4 buffer benchmark (size: 16366) 138.5 µs 97.6 µs +41.88%
md4 buffer benchmark (size: 16368) 138.5 µs 97.7 µs +41.79%
md4 buffer benchmark (size: 16370) 138.5 µs 97.6 µs +41.94%
md4 buffer benchmark (size: 2) 76.3 µs 35.2 µs ×2.2
md4 buffer benchmark (size: 20) 76.4 µs 32.9 µs ×2.3
md4 buffer benchmark (size: 200) 76.3 µs 35.5 µs ×2.1
md4 buffer benchmark (size: 2000) 81.9 µs 41 µs +99.91%
md4 buffer benchmark (size: 20000) 152.7 µs 111.7 µs +36.67%
md4 buffer benchmark (size: 2002) 81.9 µs 41 µs +99.9%
md4 buffer benchmark (size: 40) 73.6 µs 32.7 µs ×2.2
md4 buffer benchmark (size: 400) 77.1 µs 36.1 µs ×2.1
... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

@hai-x hai-x marked this pull request as ready for review May 15, 2025 18:12
@hai-x hai-x changed the title feat: eliminate dead statements in certain scenarios fix: eliminate unused statements in certain scenarios May 15, 2025
@xiaoxiaojx
Copy link
Member

/lgtm

@alexander-akait alexander-akait merged commit bca3d40 into webpack:main May 20, 2025
64 of 65 checks passed
@hai-x hai-x deleted the feat-dead-statement branch June 1, 2025 05:10
3ru pushed a commit to 3ru/webpack that referenced this pull request Jul 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unsupported import.meta methods to not replaced since v5.99

3 participants