thiserror_impl/
prop.rs

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