From c023ba67e07283bad3a43ef9bcc0336c94aa42ae Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Mon, 17 Nov 2025 08:57:54 +0000 Subject: [PATCH] fix(semantic): do not duplicate statements in temp `Vec` when binding `TSModuleDeclaration`s (#15724) I *think* this was a bug, but I don't really understand the logic, so not 100% sure. When binding a `TSModuleDeclaration`, references to all its statements are collected into a `Vec` called `module_declaration_stmts`: https://github.com/oxc-project/oxc/blob/4608549fc7715ee76c4a6d7a78069ff339f52267/crates/oxc_semantic/src/binder.rs#L434-L454 But `module_declaration_stmts.extend(block.body.iter())` is *inside* the loop, meaning that *all* the statements are inserted over and over until a statement is found which instantiates the namespace. e.g. this would push 25 `&Statement`s into the `Vec`: ```ts namespace X { type A = string; type B = number; type C = string; type D = number; interface E {} } ``` The number of entries pushed to the `Vec` rises exponentially with the number of statements in the namespace (100 statements -> 10,000 entries pushed). I assume this is unintentional. Move `module_declaration_stmts.extend(block.body.iter());` to before the loop. --- Do the `&Statement`s need to be pushed before the loop? Could it be delayed until after? That would be more performant. Though, as I said, I don't understand the logic, so I'm not sure if that'd break it. --- crates/oxc_semantic/src/binder.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/oxc_semantic/src/binder.rs b/crates/oxc_semantic/src/binder.rs index 9378da62c094b..29f57a235c0ac 100644 --- a/crates/oxc_semantic/src/binder.rs +++ b/crates/oxc_semantic/src/binder.rs @@ -433,9 +433,10 @@ fn get_module_instance_state_impl<'a, 'b>( // A module is uninstantiated if it contains only specific declarations let state = match body { TSModuleDeclarationBody::TSModuleBlock(block) => { + module_declaration_stmts.extend(block.body.iter()); + let mut child_state = ModuleInstanceState::NonInstantiated; for stmt in &block.body { - module_declaration_stmts.extend(block.body.iter()); child_state = get_module_instance_state_for_statement( builder, stmt,