ktrs_compose/rules/
content_trailing_lambda.rs1use ktrs_ast::Ast;
4use ktrs_ast::psi::{KtFunction, containing_kt_file};
5
6use crate::core::compose_kt_config::ComposeKtConfig;
7use crate::core::compose_kt_visitor::ComposeKtVisitor;
8use crate::core::emitter::Emitter;
9use crate::core::util::lambdas::{composable_lambda_types, is_composable_lambda, lambda_types};
10
11pub struct ContentTrailingLambda;
12
13impl ComposeKtVisitor for ContentTrailingLambda {
14 fn visit_composable(&self, ast: &mut Ast, function: KtFunction, emitter: &mut dyn Emitter, config: &dyn ComposeKtConfig) {
15 let file = containing_kt_file(ast, function.node()).expect("containingKtFile").node();
16 let lambda_types = lambda_types(ast, file, config);
17 let composable_lambda_types = composable_lambda_types(ast, file, config);
18 let parameters = function.value_parameters(ast);
19 let matching: Vec<_> = parameters
20 .iter()
21 .filter(|it| it.name(ast).as_deref() == Some("content"))
22 .filter(|parameter| {
23 parameter
24 .type_reference(ast)
25 .is_some_and(|t| is_composable_lambda(ast, t, &lambda_types, &composable_lambda_types))
26 })
27 .collect();
28 let candidate = if matching.len() == 1 { Some(*matching[0]) } else { None };
30 if let Some(candidate) = candidate
31 && Some(&candidate) != parameters.last()
32 {
33 emitter.report(ast, candidate.node(), CONTENT_SHOULD_BE_TRAILING_LAMBDA, false);
34 }
35 }
36}
37
38pub const CONTENT_SHOULD_BE_TRAILING_LAMBDA: &str = "\
39A @Composable `content` parameter should be moved to be the trailing lambda in a composable function.
40See https://mrmans0n.github.io/compose-rules/rules/#slots-for-main-content-should-be-the-trailing-lambda for more information.";