Skip to main content

reinhardt_admin_cli/migrate_v2/rules/
use_effect_deps.rs

1//! Rule §6.1 (4): `use_effect(closure)` →
2//! `use_effect(closure, compile_error!("add deps"))`.
3//!
4//! Per spec footnote, (4) is semi-automatic: the rule inserts a
5//! `compile_error!` placeholder as the new deps argument so the human
6//! must consciously add the dependency tuple. Auto-inferring deps would
7//! lock in subtle bugs (forgotten closures, accidental captures).
8
9use syn::visit_mut::{self, VisitMut};
10
11use crate::migrate_v2::rewriter::FileRewriter;
12
13/// `use_effect_deps` rule entry.
14pub struct Rule;
15
16impl FileRewriter for Rule {
17	fn name(&self) -> &'static str {
18		"use_effect_deps"
19	}
20
21	fn rewrite(&self, mut file: syn::File) -> syn::File {
22		HookVisitor.visit_file_mut(&mut file);
23		file
24	}
25}
26
27struct HookVisitor;
28
29/// Hooks whose v2 signature requires an explicit deps tuple as the final arg.
30const HOOKS_REQUIRING_DEPS: &[&str] = &[
31	"use_effect",
32	"use_layout_effect",
33	"use_memo",
34	"use_callback",
35	"use_callback_with",
36];
37
38impl VisitMut for HookVisitor {
39	fn visit_expr_call_mut(&mut self, c: &mut syn::ExprCall) {
40		// Match the bare path of the call.
41		if let syn::Expr::Path(p) = &*c.func
42			&& let Some(seg) = p.path.segments.last()
43		{
44			let name = seg.ident.to_string();
45			if HOOKS_REQUIRING_DEPS.contains(&name.as_str()) && c.args.len() == 1 {
46				let placeholder: syn::Expr = syn::parse_quote! {
47					compile_error!(
48						"manouche-v2 codemod: add explicit deps tuple here, e.g. `(count.clone(),)`"
49					)
50				};
51				c.args.push(placeholder);
52			}
53		}
54		visit_mut::visit_expr_call_mut(self, c);
55	}
56}