Skip to main content

fallow_cli/report/ci/
mod.rs

1pub mod diff_filter;
2pub mod pr_comment;
3pub mod review;
4pub(crate) mod suggestion;
5
6pub(crate) const TYPE_AWARE_INCOMPLETE_MESSAGE: &str =
7    "Required type-aware analysis was incomplete; this result is not a clean semantic report.";
8
9const SAVED_TYPE_AWARE_META_POINTERS: [&str; 4] = [
10    "/_meta/type_aware",
11    "/_meta/check/type_aware",
12    "/check/_meta/type_aware",
13    "/dead_code/_meta/type_aware",
14];
15
16pub(crate) fn saved_type_aware_metadata(
17    envelope: &serde_json::Value,
18) -> Result<Vec<fallow_types::envelope::TypeAwareMeta>, String> {
19    let mut metadata = Vec::new();
20    for pointer in SAVED_TYPE_AWARE_META_POINTERS {
21        let Some(value) = envelope.pointer(pointer).filter(|value| !value.is_null()) else {
22            continue;
23        };
24        let meta = serde_json::from_value(value.clone()).map_err(|error| {
25            format!(
26                "saved type-aware metadata at `{pointer}` is incompatible with this Fallow version: {error}"
27            )
28        })?;
29        metadata.push(meta);
30    }
31    Ok(metadata)
32}
33
34#[must_use]
35pub(crate) fn required_type_aware_incomplete(
36    meta: Option<&fallow_types::envelope::TypeAwareMeta>,
37) -> bool {
38    let Some(meta) = meta else {
39        return false;
40    };
41    if meta.required_completeness
42        != Some(fallow_types::semantic::SemanticCompletenessRequirement::Complete)
43    {
44        return false;
45    }
46    meta.identity.as_ref().is_none_or(|identity| {
47        identity.completeness != fallow_types::semantic::SemanticCompleteness::Complete
48    }) || meta
49        .queries
50        .iter()
51        .any(|query| query.status != fallow_types::semantic::SemanticCompleteness::Complete)
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use fallow_types::envelope::TypeAwareMeta;
58    use fallow_types::semantic::SemanticCompletenessRequirement;
59
60    #[test]
61    fn sarif_fingerprint_is_stable_for_whitespace_only_snippet_changes() {
62        let a = fallow_output::sarif_finding_fingerprint(
63            "fallow/unused-export",
64            "src/a.ts",
65            "  export const x = 1;  ",
66            14,
67        );
68        let b = fallow_output::sarif_finding_fingerprint(
69            "fallow/unused-export",
70            "src/a.ts",
71            "\nexport const x = 1;\n",
72            14,
73        );
74        assert_eq!(a, b);
75    }
76
77    #[test]
78    fn required_type_aware_metadata_without_identity_is_incomplete() {
79        let meta = TypeAwareMeta {
80            required_completeness: Some(SemanticCompletenessRequirement::Complete),
81            ..TypeAwareMeta::default()
82        };
83        assert!(required_type_aware_incomplete(Some(&meta)));
84    }
85}