xbp_analysis/application/rules/
unobserved_task.rs1use super::{async_caps, concepts_of, finding_from_concept};
2use crate::domain::concepts::ConceptKind;
3use crate::domain::model::LanguageModel;
4use crate::domain::rule::{Rule, RuleMeta};
5use crate::domain::types::{Confidence, Finding, Severity};
6use std::sync::OnceLock;
7
8#[derive(Default)]
9pub struct UnobservedTaskFailureRule;
10
11impl Rule for UnobservedTaskFailureRule {
12 fn meta(&self) -> &RuleMeta {
13 static META: OnceLock<RuleMeta> = OnceLock::new();
14 META.get_or_init(|| RuleMeta {
15 id: "unobserved-task-failure".into(),
16 name: "Unobserved task failure".into(),
17 description: "Background task detached without observing its error result.".into(),
18 default_severity: Severity::High,
19 required_capabilities: async_caps(),
20 autofix_safe: false,
21 })
22 }
23
24 fn evaluate(&self, model: &LanguageModel) -> Vec<Finding> {
25 concepts_of(model, ConceptKind::UnobservedTask)
26 .map(|(lang, c)| {
27 finding_from_concept(
28 &self.meta().id,
29 lang,
30 c,
31 Severity::High,
32 Confidence::Medium,
33 "Background task is spawned without observing completion/errors.",
34 "Task panics or errors are lost; partial work continues unnoticed.",
35 "Join the handle, use a supervisor, or log JoinError in a dedicated task.",
36 )
37 })
38 .collect()
39 }
40}