ktrs_compose/rules/
remember_state_missing.rs1use ktrs_ast::Ast;
4use ktrs_ast::psi::{KtCallExpression, KtFunction};
5
6use crate::core::compose_kt_config::ComposeKtConfig;
7use crate::core::compose_kt_visitor::ComposeKtVisitor;
8use crate::core::emitter::Emitter;
9use crate::core::util::composables::is_remembered;
10use crate::core::util::psi_elements::find_all_children;
11
12pub struct RememberStateMissing;
13
14const METHODS_THAT_NEED_REMEMBERING: &[&str] = &[
15 "derivedStateOf",
16 "mutableStateOf",
17 "mutableIntStateOf",
18 "mutableFloatStateOf",
19 "mutableDoubleStateOf",
20 "mutableLongStateOf",
21 "mutableIntListOf",
22 "mutableLongListOf",
23 "mutableFloatListOf",
24 "mutableIntSetOf",
25 "mutableLongSetOf",
26 "mutableFloatSetOf",
27 "mutableIntIntMapOf",
28 "mutableIntLongMapOf",
29 "mutableIntFloatMapOf",
30 "mutableLongIntMapOf",
31 "mutableLongLongMapOf",
32 "mutableLongFloatMapOf",
33 "mutableFloatIntMapOf",
34 "mutableFloatLongMapOf",
35 "mutableFloatFloatMapOf",
36];
37
38fn callee_text(ast: &Ast, call: KtCallExpression) -> Option<String> {
39 call.callee_expression(ast).map(|c| ast.text(c))
40}
41
42impl ComposeKtVisitor for RememberStateMissing {
43 fn visit_composable(&self, ast: &mut Ast, function: KtFunction, emitter: &mut dyn Emitter, _config: &dyn ComposeKtConfig) {
44 let not_remembered: Vec<(KtCallExpression, String)> = find_all_children::<KtCallExpression>(ast, function.node())
45 .into_iter()
46 .filter_map(|it| callee_text(ast, it).filter(|c| METHODS_THAT_NEED_REMEMBERING.contains(&c.as_str())).map(|c| (it, c)))
47 .filter(|(it, _)| !is_remembered(ast, *it, function.node()))
48 .collect();
49 for (call_expression, name) in not_remembered {
50 emitter.report(ast, call_expression.node(), &error_message(&name), false);
51 }
52 }
53}
54
55pub fn error_message(name: &str) -> String {
56 format!(
57 "Using `{name}` in a @Composable function without it being inside of a remember function.\n\
58 If you don't remember the state instance, a new state instance will be created when the function is recomposed.\n\
59 See https://mrmans0n.github.io/compose-rules/rules/#state-should-be-remembered-in-composables for more information."
60 )
61}