abi_stable/abi_stability/abi_checking/
errors.rs

1use super::*;
2
3use core_extensions::StringExt;
4
5/// An individual error from checking the layout of some type.
6#[derive(Debug, PartialEq, Clone)]
7pub enum AbiInstability {
8    ReentrantLayoutCheckingCall,
9    CyclicTypeChecking {
10        interface: &'static TypeLayout,
11        implementation: &'static TypeLayout,
12    },
13    NonZeroness(ExpectedFound<bool>),
14    Name(ExpectedFound<FmtFullType>),
15    Package(ExpectedFound<RStr<'static>>),
16    PackageVersionParseError(ParseVersionError),
17    PackageVersion(ExpectedFound<VersionStrings>),
18    MismatchedPrefixSize(ExpectedFound<u8>),
19    Size(ExpectedFound<usize>),
20    Alignment(ExpectedFound<usize>),
21    GenericParamCount(ExpectedFound<FmtFullType>),
22    TLDataDiscriminant(ExpectedFound<TLDataDiscriminant>),
23    MismatchedPrimitive(ExpectedFound<TLPrimitive>),
24    FieldCountMismatch(ExpectedFound<usize>),
25    FieldLifetimeMismatch(ExpectedFound<TLField>),
26    FnLifetimeMismatch(ExpectedFound<TLFunction>),
27    FnQualifierMismatch(ExpectedFound<TLFunction>),
28    UnexpectedField(ExpectedFound<TLField>),
29    TooManyVariants(ExpectedFound<usize>),
30    MismatchedPrefixConditionality(ExpectedFound<FieldConditionality>),
31    MismatchedExhaustiveness(ExpectedFound<IsExhaustive>),
32    MismatchedConstParam(ExpectedFound<ConstGeneric>),
33    UnexpectedVariant(ExpectedFound<RStr<'static>>),
34    ReprAttr(ExpectedFound<ReprAttr>),
35    EnumDiscriminant(ExpectedFound<TLDiscriminant>),
36    IncompatibleWithNonExhaustive(IncompatibleWithNonExhaustive),
37    NoneExtraChecks,
38    ExtraCheckError(CmpIgnored<ExtraCheckError>),
39    TagError {
40        err: TagErrors,
41    },
42}
43
44#[derive(Debug, Clone)]
45pub struct ExtraCheckError {
46    pub err: RArc<RBoxError>,
47    pub expected_err: ExpectedFound<RArc<RBoxError>>,
48}
49
50use self::AbiInstability as AI;
51
52#[allow(dead_code)]
53impl AbiInstabilityErrors {
54    #[cfg(feature = "testing")]
55    pub fn flatten_errors(&self) -> RVec<AbiInstability> {
56        self.flattened_errors().collect::<RVec<AbiInstability>>()
57    }
58
59    #[cfg(feature = "testing")]
60    pub fn flattened_errors(&self) -> impl Iterator<Item = AbiInstability> + '_ {
61        self.errors.iter().flat_map(|x| &x.errs).cloned()
62    }
63}
64
65impl std::error::Error for AbiInstabilityErrors {}
66
67impl fmt::Debug for AbiInstabilityErrors {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        fmt::Display::fmt(self, f)
70    }
71}
72impl fmt::Display for AbiInstabilityErrors {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        writeln!(
75            f,
76            "Compared <this>:\n{}\nTo <other>:\n{}\n",
77            self.interface.to_string().left_padder(4),
78            self.implementation.to_string().left_padder(4),
79        )?;
80        for err in &self.errors {
81            fmt::Display::fmt(err, f)?;
82        }
83        Ok(())
84    }
85}
86
87impl fmt::Display for AbiInstabilityError {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        let mut extra_err = None::<String>;
90
91        write!(f, "{} error(s)", self.errs.len())?;
92        if self.stack_trace.is_empty() {
93            writeln!(f, ".")?;
94        } else {
95            writeln!(f, "inside:\n    <other>\n")?;
96        }
97        for field in &self.stack_trace {
98            writeln!(f, "{}\n", field.found.to_string().left_padder(4))?;
99        }
100        if let Some(ExpectedFound { expected, found }) = self.stack_trace.last() {
101            writeln!(
102                f,
103                "Layout of expected type:\n{}\n\n\
104                 Layout of found type:\n{}\n",
105                expected.formatted_layout().left_padder(4),
106                found.formatted_layout().left_padder(4),
107            )?;
108        }
109        writeln!(f)?;
110
111        for err in &self.errs {
112            let pair = match err {
113                AI::ReentrantLayoutCheckingCall => ("reentrant layout checking call", None),
114                AI::CyclicTypeChecking { interface, .. } => {
115                    extra_err = Some(format!("The type:\n{}", interface));
116
117                    (
118                        "Attempted to check the layout of a type while checking the layout \
119                         of one of it's const parameters/extra_checks\
120                         (not necessarily a direct one).",
121                        None,
122                    )
123                }
124                AI::NonZeroness(v) => ("mismatched non-zeroness", v.display_str()),
125                AI::Name(v) => ("mismatched type", v.display_str()),
126                AI::Package(v) => ("mismatched package", v.display_str()),
127                AI::PackageVersionParseError(v) => {
128                    let expected = "a valid version string".to_string();
129                    let found = format!("{:#?}", v);
130
131                    (
132                        "could not parse version string",
133                        Some(ExpectedFound { expected, found }),
134                    )
135                }
136                AI::PackageVersion(v) => ("incompatible package versions", v.display_str()),
137                AI::MismatchedPrefixSize(v) => {
138                    ("prefix-types have a different prefix", v.display_str())
139                }
140                AI::Size(v) => ("incompatible type size", v.display_str()),
141                AI::Alignment(v) => ("incompatible type alignment", v.display_str()),
142                AI::GenericParamCount(v) => {
143                    ("incompatible amount of generic parameters", v.display_str())
144                }
145
146                AI::TLDataDiscriminant(v) => ("incompatible data ", v.debug_str()),
147                AI::MismatchedPrimitive(v) => ("incompatible primitive", v.debug_str()),
148                AI::FieldCountMismatch(v) => ("too many fields", v.display_str()),
149                AI::FnLifetimeMismatch(v) => (
150                    "function pointers reference different lifetimes",
151                    v.display_str(),
152                ),
153                AI::FnQualifierMismatch(v) => (
154                    "function pointers have different qualifiers (`unsafe`, etc.)",
155                    v.display_str(),
156                ),
157                AI::FieldLifetimeMismatch(v) => {
158                    ("field references different lifetimes", v.display_str())
159                }
160                AI::UnexpectedField(v) => ("unexpected field", v.display_str()),
161                AI::TooManyVariants(v) => ("too many variants", v.display_str()),
162                AI::MismatchedPrefixConditionality(v) => (
163                    "prefix fields differ in whether they are conditional",
164                    v.debug_str(),
165                ),
166                AI::MismatchedExhaustiveness(v) => {
167                    ("enums differ in whether they are exhaustive", v.debug_str())
168                }
169                AI::MismatchedConstParam(v) => {
170                    ("The cconst parameters are different", v.debug_str())
171                }
172                AI::UnexpectedVariant(v) => ("unexpected variant", v.debug_str()),
173                AI::ReprAttr(v) => ("incompatible repr attributes", v.debug_str()),
174                AI::EnumDiscriminant(v) => ("different discriminants", v.debug_str()),
175                AI::IncompatibleWithNonExhaustive(e) => {
176                    extra_err = Some(e.to_string());
177
178                    ("", None)
179                }
180                AI::NoneExtraChecks => {
181                    let msg = "\
182                        Interface contains a value in `extra_checks` \
183                        while the implementation does not.\
184                    ";
185                    (msg, None)
186                }
187                AI::ExtraCheckError(ec_error) => {
188                    let ExtraCheckError { err, expected_err } = &**ec_error;
189                    extra_err = Some((**err).to_string());
190
191                    ("", expected_err.display_str())
192                }
193                AI::TagError { err } => {
194                    extra_err = Some(err.to_string());
195
196                    ("", None)
197                }
198            };
199
200            let (error_msg, expected_err): (&'static str, Option<ExpectedFound<String>>) = pair;
201
202            if let Some(expected_err) = expected_err {
203                writeln!(
204                    f,
205                    "\nError:{}\nExpected:\n{}\nFound:\n{}",
206                    error_msg,
207                    expected_err.expected.left_padder(4),
208                    expected_err.found.left_padder(4),
209                )?;
210            }
211            if let Some(extra_err) = &extra_err {
212                writeln!(f, "\nExtra:\n{}\n", extra_err.left_padder(4))?;
213            }
214        }
215        Ok(())
216    }
217}
218
219/// All the errors from checking the layout of every nested type in TypeLayout.
220#[derive(Clone, PartialEq)]
221#[repr(C)]
222pub struct AbiInstabilityErrors {
223    pub interface: &'static TypeLayout,
224    pub implementation: &'static TypeLayout,
225    pub errors: RVec<AbiInstabilityError>,
226    pub(super) _priv: (),
227}
228
229/// All the shallow errors from checking an individual type.
230///
231/// Error that happen lower or higher on the stack are stored in separate
232///  `AbiInstabilityError`s.
233#[derive(Debug, Clone, PartialEq)]
234#[repr(C)]
235pub struct AbiInstabilityError {
236    pub stack_trace: RVec<ExpectedFound<TLFieldOrFunction>>,
237    pub errs: RVec<AbiInstability>,
238    pub index: usize,
239    pub(super) _priv: (),
240}