Skip to content

Commit 7e26eb8

Browse files
committed
[react-compiler-rust] Fix unused import { c as _c } when no compiled function uses the cache
Remove premature memo cache import registration from pipeline.rs and the opt-out path in program.rs. Previously, when a function compiled with memo slots but was later discarded (e.g., due to "use no memo" opt-out or validation errors), its import registration persisted on the ProgramContext. If other functions in the same file compiled to 0 memo slots, the stale import would be emitted in the final output even though _c was never used. The import is now only registered in apply_compiled_functions() when at least one actually-applied function has memo_slots_used > 0, matching the intended behavior.
1 parent 49003e8 commit 7e26eb8

4 files changed

Lines changed: 99 additions & 43 deletions

File tree

compiler/crates/react_compiler/src/entrypoint/pipeline.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,13 +1055,12 @@ pub fn compile_fn(
10551055
)?;
10561056
context.timing.stop();
10571057

1058-
// Register the memo cache import as a side effect of codegen, matching TS behavior
1059-
// where addMemoCacheImport() is called during codegenReactiveFunction. This must happen
1060-
// BEFORE the env.has_errors() check so the import persists even when the pipeline
1061-
// returns Err (e.g., when validation errors are accumulated but codegen succeeded).
1062-
if codegen_result.memo_slots_used > 0 {
1063-
context.add_memo_cache_import();
1064-
}
1058+
// NOTE: we intentionally do NOT register the memo cache import here.
1059+
// The import is registered in apply_compiled_functions() only for functions
1060+
// that are actually applied to the output. Registering it here would cause
1061+
// a spurious `import { c as _c }` when a function compiles with memo slots
1062+
// but is later discarded (e.g., due to "use no memo" opt-out or errors),
1063+
// while other functions in the same file compile to 0 memo slots.
10651064

10661065
if env.config.validate_source_locations {
10671066
super::validate_source_locations::validate_source_locations(

compiler/crates/react_compiler/src/entrypoint/program.rs

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ fn returns_non_node_in_stmt(stmt: &Statement, result: &mut bool) {
425425
returns_non_node_in_stmt(&if_stmt.consequent, result);
426426
if let Some(ref alt) = if_stmt.alternate {
427427
returns_non_node_in_stmt(alt, result);
428-
}
428+
}
429429
}
430430
Statement::ForStatement(for_stmt) => returns_non_node_in_stmt(&for_stmt.body, result),
431431
Statement::WhileStatement(while_stmt) => returns_non_node_in_stmt(&while_stmt.body, result),
@@ -1573,13 +1573,9 @@ fn process_fn(
15731573
reason: format!("Skipped due to '{}' directive.", opt_out_value),
15741574
loc: opt_out.and_then(|d| to_logger_loc(d.base.loc.as_ref(), source_filename)),
15751575
});
1576-
// Even though the function is skipped, register the memo cache import
1577-
// if the compiled function had memo slots. This matches TS behavior where
1578-
// addMemoCacheImport() is called during codegen as a side effect that
1579-
// persists even when the function is later skipped.
1580-
if codegen_fn.memo_slots_used > 0 {
1581-
context.add_memo_cache_import();
1582-
}
1576+
// The function is skipped due to opt-out. Do NOT register the memo
1577+
// cache import here — it will be registered in apply_compiled_functions()
1578+
// only for functions that are actually applied to the output.
15831579
return Ok(None);
15841580
}
15851581

@@ -3204,12 +3200,8 @@ fn insert_after_fn_recursive(stmts: &mut Vec<Statement>, start: u32, new_stmt: S
32043200

32053201
fn insert_after_fn_in_stmt(stmt: &mut Statement, start: u32, new_stmt: &Statement) -> bool {
32063202
match stmt {
3207-
Statement::FunctionDeclaration(f) => {
3208-
insert_after_fn_in_block(&mut f.body, start, new_stmt)
3209-
}
3210-
Statement::BlockStatement(b) => {
3211-
insert_after_fn_in_block(b, start, new_stmt)
3212-
}
3203+
Statement::FunctionDeclaration(f) => insert_after_fn_in_block(&mut f.body, start, new_stmt),
3204+
Statement::BlockStatement(b) => insert_after_fn_in_block(b, start, new_stmt),
32133205
Statement::ExpressionStatement(e) => {
32143206
insert_after_fn_in_expr(&mut e.expression, start, new_stmt)
32153207
}
@@ -3231,14 +3223,18 @@ fn insert_after_fn_in_stmt(stmt: &mut Statement, start: u32, new_stmt: &Statemen
32313223
false
32323224
}
32333225
Statement::ExportDefaultDeclaration(e) => match e.declaration.as_mut() {
3234-
ExportDefaultDecl::FunctionDeclaration(f) => insert_after_fn_in_block(&mut f.body, start, new_stmt),
3226+
ExportDefaultDecl::FunctionDeclaration(f) => {
3227+
insert_after_fn_in_block(&mut f.body, start, new_stmt)
3228+
}
32353229
ExportDefaultDecl::Expression(expr) => insert_after_fn_in_expr(expr, start, new_stmt),
32363230
_ => false,
32373231
},
32383232
Statement::ExportNamedDeclaration(e) => {
32393233
if let Some(decl) = &mut e.declaration {
32403234
match decl.as_mut() {
3241-
Declaration::FunctionDeclaration(f) => insert_after_fn_in_block(&mut f.body, start, new_stmt),
3235+
Declaration::FunctionDeclaration(f) => {
3236+
insert_after_fn_in_block(&mut f.body, start, new_stmt)
3237+
}
32423238
Declaration::VariableDeclaration(v) => {
32433239
for d in &mut v.declarations {
32443240
if let Some(init) = &mut d.init {
@@ -3257,26 +3253,42 @@ fn insert_after_fn_in_stmt(stmt: &mut Statement, start: u32, new_stmt: &Statemen
32573253
}
32583254
Statement::IfStatement(i) => {
32593255
insert_after_fn_in_stmt(&mut i.consequent, start, new_stmt)
3260-
|| i.alternate.as_mut().map_or(false, |a| insert_after_fn_in_stmt(a, start, new_stmt))
3256+
|| i.alternate
3257+
.as_mut()
3258+
.map_or(false, |a| insert_after_fn_in_stmt(a, start, new_stmt))
32613259
}
32623260
Statement::ForStatement(f) => insert_after_fn_in_stmt(&mut f.body, start, new_stmt),
32633261
Statement::WhileStatement(w) => insert_after_fn_in_stmt(&mut w.body, start, new_stmt),
32643262
Statement::TryStatement(t) => {
3265-
if insert_after_fn_in_block(&mut t.block, start, new_stmt) { return true; }
3263+
if insert_after_fn_in_block(&mut t.block, start, new_stmt) {
3264+
return true;
3265+
}
32663266
if let Some(h) = &mut t.handler {
3267-
if insert_after_fn_in_block(&mut h.body, start, new_stmt) { return true; }
3267+
if insert_after_fn_in_block(&mut h.body, start, new_stmt) {
3268+
return true;
3269+
}
32683270
}
32693271
if let Some(f) = &mut t.finalizer {
3270-
if insert_after_fn_in_block(f, start, new_stmt) { return true; }
3272+
if insert_after_fn_in_block(f, start, new_stmt) {
3273+
return true;
3274+
}
32713275
}
32723276
false
32733277
}
32743278
_ => false,
32753279
}
32763280
}
32773281

3278-
fn insert_after_fn_in_block(block: &mut react_compiler_ast::statements::BlockStatement, start: u32, new_stmt: &Statement) -> bool {
3279-
if let Some(pos) = block.body.iter().position(|s| stmt_has_fn_at_start(s, start)) {
3282+
fn insert_after_fn_in_block(
3283+
block: &mut react_compiler_ast::statements::BlockStatement,
3284+
start: u32,
3285+
new_stmt: &Statement,
3286+
) -> bool {
3287+
if let Some(pos) = block
3288+
.body
3289+
.iter()
3290+
.position(|s| stmt_has_fn_at_start(s, start))
3291+
{
32803292
block.body.insert(pos + 1, new_stmt.clone());
32813293
return true;
32823294
}
@@ -3288,7 +3300,11 @@ fn insert_after_fn_in_block(block: &mut react_compiler_ast::statements::BlockSta
32883300
false
32893301
}
32903302

3291-
fn insert_after_fn_in_expr(expr: &mut react_compiler_ast::expressions::Expression, start: u32, new_stmt: &Statement) -> bool {
3303+
fn insert_after_fn_in_expr(
3304+
expr: &mut react_compiler_ast::expressions::Expression,
3305+
start: u32,
3306+
new_stmt: &Statement,
3307+
) -> bool {
32923308
use react_compiler_ast::expressions::Expression;
32933309
match expr {
32943310
Expression::ObjectExpression(obj) => {
@@ -3299,7 +3315,9 @@ fn insert_after_fn_in_expr(expr: &mut react_compiler_ast::expressions::Expressio
32993315
return true;
33003316
}
33013317
}
3302-
react_compiler_ast::expressions::ObjectExpressionProperty::ObjectProperty(p) => {
3318+
react_compiler_ast::expressions::ObjectExpressionProperty::ObjectProperty(
3319+
p,
3320+
) => {
33033321
if insert_after_fn_in_expr(&mut p.value, start, new_stmt) {
33043322
return true;
33053323
}
@@ -3317,19 +3335,15 @@ fn insert_after_fn_in_expr(expr: &mut react_compiler_ast::expressions::Expressio
33173335
}
33183336
false
33193337
}
3320-
Expression::ArrowFunctionExpression(arrow) => {
3321-
match arrow.body.as_mut() {
3322-
react_compiler_ast::expressions::ArrowFunctionBody::BlockStatement(block) => {
3323-
insert_after_fn_in_block(block, start, new_stmt)
3324-
}
3325-
react_compiler_ast::expressions::ArrowFunctionBody::Expression(e) => {
3326-
insert_after_fn_in_expr(e, start, new_stmt)
3327-
}
3338+
Expression::ArrowFunctionExpression(arrow) => match arrow.body.as_mut() {
3339+
react_compiler_ast::expressions::ArrowFunctionBody::BlockStatement(block) => {
3340+
insert_after_fn_in_block(block, start, new_stmt)
33283341
}
3329-
}
3330-
Expression::FunctionExpression(f) => {
3331-
insert_after_fn_in_block(&mut f.body, start, new_stmt)
3332-
}
3342+
react_compiler_ast::expressions::ArrowFunctionBody::Expression(e) => {
3343+
insert_after_fn_in_expr(e, start, new_stmt)
3344+
}
3345+
},
3346+
Expression::FunctionExpression(f) => insert_after_fn_in_block(&mut f.body, start, new_stmt),
33333347
Expression::CallExpression(c) => {
33343348
for arg in &mut c.arguments {
33353349
if insert_after_fn_in_expr(arg, start, new_stmt) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @compilationMode(all)
6+
function useMyHook({a, b}) {
7+
return a + b;
8+
}
9+
10+
export const FIXTURE_ENTRYPOINT = {
11+
fn: useMyHook,
12+
params: [{a: 1, b: 2}],
13+
};
14+
15+
```
16+
17+
## Code
18+
19+
```javascript
20+
// @compilationMode(all)
21+
function useMyHook(t0) {
22+
const { a, b } = t0;
23+
return a + b;
24+
}
25+
26+
export const FIXTURE_ENTRYPOINT = {
27+
fn: useMyHook,
28+
params: [{ a: 1, b: 2 }],
29+
};
30+
31+
```
32+
33+
### Eval output
34+
(kind: ok) 3
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// @compilationMode(all)
2+
function useMyHook({a, b}) {
3+
return a + b;
4+
}
5+
6+
export const FIXTURE_ENTRYPOINT = {
7+
fn: useMyHook,
8+
params: [{a: 1, b: 2}],
9+
};

0 commit comments

Comments
 (0)