ktrs_compose/rules/
multiple_content_emitters.rs1use ktrs_ast::Ast;
4use ktrs_ast::psi::{KtFile, KtFunction, KtNamedFunction};
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::{create_direct_composable_to_emission_count_mapping, refine_composable_to_emission_count_mapping};
10use crate::core::util::kt_annotateds::is_composable;
11use crate::core::util::kt_functions::{has_any_context_arguments, has_receiver_type};
12use crate::core::util::psi_elements::find_all_children;
13
14pub struct MultipleContentEmitters;
15
16impl ComposeKtVisitor for MultipleContentEmitters {
17 fn visit_file(&self, ast: &mut Ast, file: KtFile, emitter: &mut dyn Emitter, config: &dyn ComposeKtConfig) {
18 let kotlin = config.embedded_kotlin();
19 let composables: Vec<KtFunction> = find_all_children::<KtNamedFunction>(ast, file.node())
20 .into_iter()
21 .filter(|it| is_composable(ast, it.node()))
22 .filter(|it| it.has_block_body(ast))
23 .filter(|it| !has_any_context_arguments(ast, *it, kotlin))
24 .map(|it| KtFunction::of(ast, it.node()))
25 .filter(|it| !has_receiver_type(ast, *it))
26 .collect();
27 let composable_to_emission_count = create_direct_composable_to_emission_count_mapping(ast, &composables, config);
28 let direct_emissions_reported: Vec<KtFunction> =
29 composable_to_emission_count.iter().filter(|(_, count)| *count > 1).map(|&(function, _)| function).collect();
30 for composable in &direct_emissions_reported {
31 emitter.report(ast, composable.node(), MULTIPLE_CONTENT_EMITTERS_DETECTED, false);
32 }
33 let current_mapping = refine_composable_to_emission_count_mapping(ast, composable_to_emission_count, config);
34 for (composable, _) in current_mapping
35 .into_iter()
36 .filter(|(_, count)| *count > 1)
37 .filter(|(function, _)| !direct_emissions_reported.contains(function))
38 {
39 emitter.report(ast, composable.node(), MULTIPLE_CONTENT_EMITTERS_DETECTED, false);
40 }
41 }
42}
43
44pub const MULTIPLE_CONTENT_EMITTERS_DETECTED: &str = "\
45Composable functions should only be emitting content into the composition from one source at their top level.
46See https://mrmans0n.github.io/compose-rules/rules/#do-not-emit-multiple-pieces-of-content for more information.";