thiserror_impl/
prop.rs

1use crate::ast::{Enum, Field, Struct, Variant};
2use crate::span::MemberSpan;
3use proc_macro2::Span;
4use syn::{Member, Type};
5
6impl Struct<'_> {
7    pub(crate) fn from_field(&self) -> Option<&Field> {
8        from_field(&self.fields)
9    }
10
11    pub(crate) fn source_field(&self) -> Option<&Field> {
12        source_field(&self.fields)
13    }
14
15    pub(crate) fn backtrace_field(&self) -> Option<&Field> {
16        backtrace_field(&self.fields)
17    }
18
19    pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> {
20        let backtrace_field = self.backtrace_field()?;
21        distinct_backtrace_field(backtrace_field, self.from_field())
22    }
23}
24
25impl Enum<'_> {
26    pub(crate) fn has_source(&self) -> bool {
27        self.variants
28            .iter()
29            .any(|variant| variant.source_field().is_some() || variant.attrs.transparent.is_some())
30    }
31
32    pub(crate) fn has_backtrace(&self) -> bool {
33        self.variants
34            .iter()
35            .any(|variant| variant.backtrace_field().is_some())
36    }
37
38    pub(crate) fn has_display(&self) -> bool {
39        self.attrs.display.is_some()
40            || self.attrs.transparent.is_some()
41            || self
42                .variants
43                .iter()
44                .any(|variant| variant.attrs.display.is_some())
45            || self
46                .variants
47                .iter()
48                .all(|variant| variant.attrs.transparent.is_some())
49    }
50}
51
52impl Variant<'_> {
53    pub(crate) fn from_field(&self) -> Option<&Field> {
54        from_field(&self.fields)
55    }
56
57    pub(crate) fn source_field(&self) -> Option<&Field> {
58        source_field(&self.fields)
59    }
60
61    pub(crate) fn backtrace_field(&self) -> Option<&Field> {
62        backtrace_field(&self.fields)
63    }
64
65    pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> {
66        let backtrace_field = self.backtrace_field()?;
67        distinct_backtrace_field(backtrace_field, self.from_field())
68    }
69}
70
71impl Field<'_> {
72    pub(crate) fn is_backtrace(&self) -> bool {
73        type_is_backtrace(self.ty)
74    }
75
76    pub(crate) fn source_span(&self) -> Span {
77        if let Some(source_attr) = &self.attrs.source {
78            source_attr.path().get_ident().unwrap().span()
79        } else if let Some(from_attr) = &self.attrs.from {
80            from_attr.path().get_ident().unwrap().span()
81        } else {
82            self.member.member_span()
83        }
84    }
85}
86
87fn from_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
88    for field in fields {
89        if field.attrs.from.is_some() {
90            return Some(field);
91        }
92    }
93    None
94}
95
96fn source_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
97    for field in fields {
98        if field.attrs.from.is_some() || field.attrs.source.is_some() {
99            return Some(field);
100        }
101    }
102    for field in fields {
103        match &field.member {
104            Member::Named(ident) if ident == "source" => return Some(field),
105            _ => {}
106        }
107    }
108    None
109}
110
111fn backtrace_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
112    for field in fields {
113        if field.attrs.backtrace.is_some() {
114            return Some(field);
115        }
116    }
117    for field in fields {
118        if field.is_backtrace() {
119            return Some(field);
120        }
121    }
122    None
123}
124
125// The #[backtrace] field, if it is not the same as the #[from] field.
126fn distinct_backtrace_field<'a, 'b>(
127    backtrace_field: &'a Field<'b>,
128    from_field: Option<&Field>,
129) -> Option<&'a Field<'b>> {
130    if from_field.map_or(false, |from_field| {
131        from_field.member == backtrace_field.member
132    }) {
133        None
134    } else {
135        Some(backtrace_field)
136    }
137}
138
139fn type_is_backtrace(ty: &Type) -> bool {
140    let path = match ty {
141        Type::Path(ty) => &ty.path,
142        _ => return false,
143    };
144
145    let last = path.segments.last().unwrap();
146    last.ident == "Backtrace" && last.arguments.is_empty()
147}