Skip to main content

fack_codegen/
target.rs

1//! Parsed derive targets and attribute collection.
2
3use alloc::vec::Vec;
4
5use proc_macro2::Span;
6use syn::{Data, DataEnum, DataStruct, DeriveInput, Variant as SynVariant, spanned::Spanned};
7
8use crate::{
9    diagnostic::Errors,
10    enumerate::{Enumeration, Variant},
11    field::{FieldRef, Fields},
12    format::Format,
13    structure::Structure,
14    syntax::{Config, Declaration, Import, Inline, Param, ParamKind, Transparent},
15    validate::ValidatedTarget,
16};
17
18/// A parsed error declaration before semantic validation.
19#[derive(Clone, Debug, PartialEq, Eq, Hash)]
20pub struct Target(Kind);
21
22/// Internal parsed target representation shared with validation.
23#[derive(Clone, Debug, PartialEq, Eq, Hash)]
24pub enum Kind {
25    /// A parsed enumeration declaration.
26    Enum(Enumeration),
27
28    /// A parsed structure declaration.
29    Struct(Structure),
30}
31
32impl Target {
33    /// Parse an error target from a derive input.
34    #[inline]
35    pub fn input(input: &DeriveInput) -> syn::Result<Self> {
36        match &input.data {
37            Data::Struct(data) => Self::structure(input, data),
38            Data::Enum(data) => Self::enumeration(input, data),
39            Data::Union(..) => Err(syn::Error::new_spanned(&input.ident, "unions cannot derive Error")),
40        }
41    }
42
43    /// Validate the parsed declaration and return proof-bearing semantics.
44    ///
45    /// # Errors
46    ///
47    /// Returns a diagnostic when declarations conflict or field references do
48    /// not satisfy their semantic requirements.
49    #[inline]
50    pub fn validate(self) -> syn::Result<ValidatedTarget> {
51        let Self(kind) = self;
52
53        crate::validate::target(kind)
54    }
55
56    /// Parse a structure target and collect each declaration at most once.
57    fn structure(input: &DeriveInput, data: &DataStruct) -> syn::Result<Self> {
58        let (params, ..) = Param::classify(&input.attrs)?;
59        let mut format = Bucket::<Format>::new(&input.ident);
60        let mut display = Bucket::<syn::Path>::new(&input.ident);
61        let mut source = Bucket::<FieldRef>::new(&input.ident);
62        let mut transparent = Bucket::<Transparent>::new(&input.ident);
63        let mut from = Bucket::<()>::new(&input.ident);
64        let mut inline = Bucket::<Inline>::new(&input.ident);
65        let mut import = Bucket::<Import>::new(&input.ident);
66
67        for param in params {
68            let (name, kind) = param.parts();
69            match kind {
70                ParamKind::Format(value) => format.push((name, value)),
71                ParamKind::Display(value) => display.push((name, value)),
72                ParamKind::Source(value) => source.push((name, value)),
73                ParamKind::Transparent(value) => transparent.push((name, value)),
74                ParamKind::From => from.push((name, ())),
75                ParamKind::Inline(value) => inline.push((name, value)),
76                ParamKind::Import(value) => import.push((name, value)),
77            }
78        }
79
80        let inline = inline.optional()?;
81        let import = import.optional()?;
82        let name = input.ident.clone();
83        let generics = input.generics.clone();
84        let fields = Fields::from_syn(&data.fields)?;
85        let format = format.optional()?;
86        let display = display.optional()?;
87        let source = source.optional()?;
88        let transparent = transparent.optional()?;
89        let from = from.optional()?.is_some();
90        let config = Config::new(inline, import);
91        let declaration = Declaration::new(format, display, source, transparent, from);
92        let structure = Structure::new(config, name, generics, fields, declaration);
93
94        Ok(Self(Kind::Struct(structure)))
95    }
96
97    /// Parse enum-level options and validate that behavior parameters stay on
98    /// variants.
99    fn enumeration(input: &DeriveInput, data: &DataEnum) -> syn::Result<Self> {
100        let (params, ..) = Param::classify(&input.attrs)?;
101        let mut inline = Bucket::<Inline>::new(&input.ident);
102        let mut import = Bucket::<Import>::new(&input.ident);
103        let mut errors = Errors::new();
104
105        for param in params {
106            let (name, kind) = param.parts();
107            match kind {
108                ParamKind::Inline(value) => inline.push((name, value)),
109                ParamKind::Import(value) => import.push((name, value)),
110                _ => errors.push(syn::Error::new_spanned(
111                    name,
112                    "only `inline` and `import` are valid on an error enum",
113                )),
114            }
115        }
116
117        let mut variants = Vec::with_capacity(data.variants.len());
118
119        for variant in &data.variants {
120            match Self::variant(variant) {
121                Ok(variant) => variants.push(variant),
122                Err(error) => errors.push(error),
123            }
124        }
125
126        let inline = inline.optional()?;
127        let import = import.optional()?;
128        let name = input.ident.clone();
129        let generics = input.generics.clone();
130        let config = Config::new(inline, import);
131        let enumeration = Enumeration::new(config, name, generics, variants);
132
133        errors.finish(Self(Kind::Enum(enumeration)))
134    }
135
136    /// Parse one enum variant and reject container-only parameters.
137    fn variant(variant: &SynVariant) -> syn::Result<Variant> {
138        let (params, ..) = Param::classify(&variant.attrs)?;
139        let mut format = Bucket::<Format>::new(&variant.ident);
140        let mut display = Bucket::<syn::Path>::new(&variant.ident);
141        let mut source = Bucket::<FieldRef>::new(&variant.ident);
142        let mut transparent = Bucket::<Transparent>::new(&variant.ident);
143        let mut from = Bucket::<()>::new(&variant.ident);
144        let mut errors = Errors::new();
145
146        for param in params {
147            let (name, kind) = param.parts();
148            match kind {
149                ParamKind::Format(value) => format.push((name, value)),
150                ParamKind::Display(value) => display.push((name, value)),
151                ParamKind::Source(value) => source.push((name, value)),
152                ParamKind::Transparent(value) => transparent.push((name, value)),
153                ParamKind::From => from.push((name, ())),
154                ParamKind::Inline(..) | ParamKind::Import(..) => {
155                    errors.push(syn::Error::new_spanned(name, "this parameter belongs on the enum"))
156                }
157            }
158        }
159
160        let name = variant.ident.clone();
161        let fields = Fields::from_syn(&variant.fields)?;
162        let format = format.optional()?;
163        let display = display.optional()?;
164        let source = source.optional()?;
165        let transparent = transparent.optional()?;
166        let from = from.optional()?.is_some();
167        let declaration = Declaration::new(format, display, source, transparent, from);
168        let variant = Variant::new(name, fields, declaration);
169
170        errors.finish(variant)
171    }
172}
173
174/// One syntax parameter constrained to at most one occurrence.
175// NOTE(invariant): `first` stores the first occurrence and `extra` stores only
176// later occurrences. `optional` is the sole transition that exposes a value.
177struct Bucket<ValueType> {
178    /// First occurrence of the syntax parameter.
179    first: Option<(Span, ValueType)>,
180
181    /// Later occurrences retained for duplicate diagnostics.
182    extra: Vec<(Span, ValueType)>,
183}
184
185impl<ValueType> Bucket<ValueType> {
186    /// Construct an empty duplicate-detection bucket.
187    const fn new<SubjectType>(_subject: &SubjectType) -> Self
188    where
189        SubjectType: Spanned,
190    {
191        let first = None;
192        let extra = Vec::new();
193
194        Self { first, extra }
195    }
196
197    /// Record one occurrence while preserving the first span for diagnostics.
198    fn push<SpanType>(&mut self, (spanned, value): (SpanType, ValueType))
199    where
200        SpanType: Spanned,
201    {
202        let Self { first, extra } = self;
203        let span = spanned.span();
204
205        if first.is_some() {
206            extra.push((span, value));
207        } else {
208            *first = Some((span, value));
209        }
210    }
211
212    /// Return the unique value or combine diagnostics for duplicate
213    /// occurrences.
214    fn optional(self) -> syn::Result<Option<ValueType>> {
215        let Self { first, extra } = self;
216
217        match first {
218            Some((_span, value)) if extra.is_empty() => Ok(Some(value)),
219            Some((span, _)) => {
220                let mut error = syn::Error::new(span, "parameter declared more than once");
221
222                for (extra_span, _) in extra {
223                    error.combine(syn::Error::new(extra_span, "duplicate parameter"));
224                }
225
226                Err(error)
227            }
228            None => Ok(None),
229        }
230    }
231}