ktrs_compose/rules/
composable_annotation_naming.rs1use ktrs_ast::Ast;
4use ktrs_ast::psi::KtClass;
5
6use crate::core::compose_kt_config::ComposeKtConfig;
7use crate::core::compose_kt_visitor::ComposeKtVisitor;
8use crate::core::emitter::Emitter;
9
10pub struct ComposableAnnotationNaming;
11
12impl ComposeKtVisitor for ComposableAnnotationNaming {
13 fn visit_class(&self, ast: &mut Ast, clazz: KtClass, emitter: &mut dyn Emitter, _config: &dyn ComposeKtConfig) {
14 if !clazz.is_annotation(ast) {
15 return;
16 }
17 if !is_composable_target_marker_annotation(ast, clazz) {
18 return;
19 }
20 let name = clazz.name_as_safe_name(ast);
21 if !name.ends_with("Composable") {
22 emitter.report(ast, clazz.node(), COMPOSABLE_ANNOTATION_DOES_NOT_END_WITH_COMPOSABLE, false);
23 }
24 }
25}
26
27fn is_composable_target_marker_annotation(ast: &Ast, clazz: KtClass) -> bool {
29 clazz
30 .annotation_entries(ast)
31 .into_iter()
32 .any(|it| it.callee_expression(ast).is_some_and(|c| ast.text(c).contains("ComposableTargetMarker")))
33}
34
35pub const COMPOSABLE_ANNOTATION_DOES_NOT_END_WITH_COMPOSABLE: &str = "\
36Composable annotations (e.g. tagged with `@ComposableTargetMarker`) should have the `Composable` suffix.
37See https://mrmans0n.github.io/compose-rules/rules/#naming-composable-annotations-properly for more information.";