Skip to main content

xbp_analysis/application/rules/
error_context_loss.rs

1use 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}