xbp_analysis/application/rules/
error_context_loss.rs1use super::{base_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 ErrorContextLossRule;
10
11impl Rule for ErrorContextLossRule {
12 fn meta(&self) -> &RuleMeta {
13 static META: OnceLock<RuleMeta> = OnceLock::new();
14 META.get_or_init(|| RuleMeta {
15 id: "error-context-loss".into(),
16 name: "Error context loss".into(),
17 description: "Original error context is discarded when mapping/wrapping.".into(),
18 default_severity: Severity::Medium,
19 required_capabilities: base_caps(),
20 autofix_safe: false,
21 })
22 }
23
24 fn evaluate(&self, model: &LanguageModel) -> Vec<Finding> {
25 concepts_of(model, ConceptKind::ContextLoss)
26 .chain(
27 concepts_of(model, ConceptKind::Propagation)
28 .filter(|(_, c)| c.drops_context),
29 )
30 .map(|(lang, c)| {
31 finding_from_concept(
32 &self.meta().id,
33 lang,
34 c,
35 Severity::Medium,
36 Confidence::Medium,
37 "Error context is dropped during mapping or conversion.",
38 "Debugging loses the root cause; only a generic error remains.",
39 "Chain sources (anyhow/thiserror source, cause(), wrap with context).",
40 )
41 })
42 .collect()
43 }
44}