Skip to main content

fack_codegen/
validate.rs

1//! Proof transition from parsed declarations to validated semantics.
2
3use alloc::{boxed::Box, vec::Vec};
4
5use proc_macro2::TokenStream;
6use syn::{Ident, Path};
7
8use crate::{
9    enumerate,
10    field::{FieldRef, Fields},
11    format::Format,
12    resolve::Resolve as _,
13    semantics::{
14        Conversion, Display, Enumeration as ValidEnumeration, ErrorSource, Header, Structure as ValidStructure, Target as ValidTarget,
15        Variant as ValidVariant,
16    },
17    source::{Source, SourceShape},
18    structure,
19    syntax::Transparent,
20    target::Kind,
21};
22
23/// Proof that an internal value passed its [`Validate`] transition.
24// NOTE(invariant): The wrapped value can only be created by this module after
25// the corresponding validation routine succeeds. Consumers may rely on the
26// semantic checks performed by that transition.
27#[derive(Clone, Debug)]
28struct Validated<ValueType>(ValueType);
29
30impl<ValueType> Validated<ValueType> {
31    /// Wrap a value only at a validation boundary owned by this module.
32    const fn new(value: ValueType) -> Self {
33        Self(value)
34    }
35
36    /// Consume the proof and return the validated value.
37    #[inline]
38    #[must_use]
39    fn into_inner(self) -> ValueType {
40        let Self(value) = self;
41
42        value
43    }
44}
45
46/// Validate a parsed representation into a stronger semantic representation.
47trait Validate {
48    /// Stronger representation produced after validation succeeds.
49    type Output;
50
51    /// Validate the parsed value.
52    ///
53    /// # Errors
54    ///
55    /// Returns a diagnostic when declarations conflict or field references do
56    /// not satisfy their semantic requirements.
57    fn validate(self) -> syn::Result<Self::Output>;
58}
59
60/// A validated derive target that can be expanded safely.
61// NOTE(invariant): Construction succeeds only after the complete parsed target
62// has passed semantic validation. Expansion may rely on those checks without
63// repeating them.
64#[derive(Clone, Debug)]
65pub struct ValidatedTarget(ValidTarget);
66
67impl ValidatedTarget {
68    /// Expand the validated target into Rust implementation tokens.
69    ///
70    /// # Errors
71    ///
72    /// Returns a diagnostic if final token construction discovers an invalid
73    /// generated Rust fragment.
74    #[inline]
75    pub fn expand(self) -> syn::Result<TokenStream> {
76        let Self(target) = self;
77
78        crate::expand::target(target)
79    }
80}
81
82/// Validate one parsed target kind and establish the public proof state.
83pub fn target(target: Kind) -> syn::Result<ValidatedTarget> {
84    let target = match target {
85        Kind::Struct(structure) => {
86            let structure = structure.validate()?.into_inner();
87
88            ValidTarget::Struct(Box::new(structure))
89        }
90        Kind::Enum(enumeration) => {
91            let enumeration = enumeration.validate()?.into_inner();
92
93            ValidTarget::Enum(enumeration)
94        }
95    };
96
97    Ok(ValidatedTarget(target))
98}
99
100impl Validate for structure::Structure {
101    /// Proof-bearing structure semantics produced after declaration checks.
102    type Output = Validated<ValidStructure>;
103
104    /// Resolve one parsed structure into validated display, source, and
105    /// conversion behavior.
106    #[inline]
107    fn validate(self) -> syn::Result<Self::Output> {
108        let (config, name, generics, fields, declaration) = self.parts();
109        let (inline, import) = config.parts();
110        let (format, display, source, transparent, from) = declaration.parts();
111        let declaration = Declaration {
112            subject: name.clone(),
113            fields: &fields,
114            format,
115            display,
116            source,
117            transparent,
118            from,
119        };
120        let (display, source, conversion) = declaration.validate()?;
121        let header = Header::new(inline, import, name, generics);
122
123        let structure = ValidStructure::new(header, fields, display, source, conversion);
124
125        Ok(Validated::new(structure))
126    }
127}
128
129impl Validate for enumerate::Enumeration {
130    /// Proof-bearing enumeration semantics with every variant already
131    /// validated.
132    type Output = Validated<ValidEnumeration>;
133
134    /// Validate every variant before constructing the enumeration proof.
135    #[inline]
136    fn validate(self) -> syn::Result<Self::Output> {
137        let (config, name, generics, variants) = self.parts();
138        let (inline, import) = config.parts();
139        let mut validated = Vec::with_capacity(variants.len());
140
141        for variant in variants {
142            validated.push(variant.validate()?.into_inner());
143        }
144
145        let header = Header::new(inline, import, name, generics);
146
147        let enumeration = ValidEnumeration::new(header, validated);
148
149        Ok(Validated::new(enumeration))
150    }
151}
152
153impl Validate for enumerate::Variant {
154    /// Proof-bearing variant semantics produced after declaration checks.
155    type Output = Validated<ValidVariant>;
156
157    /// Resolve one parsed variant into validated display, source, and
158    /// conversion behavior.
159    #[inline]
160    fn validate(self) -> syn::Result<Self::Output> {
161        let (name, fields, declaration) = self.parts();
162        let (format, display, source, transparent, from) = declaration.parts();
163        let declaration = Declaration {
164            subject: name.clone(),
165            fields: &fields,
166            format,
167            display,
168            source,
169            transparent,
170            from,
171        };
172        let (display, source, conversion) = declaration.validate()?;
173
174        let variant = ValidVariant::new(name, fields, display, source, conversion);
175
176        Ok(Validated::new(variant))
177    }
178}
179
180/// One set of declarations whose semantic compatibility must be proven.
181struct Declaration<'fields> {
182    /// Syntax identifier used as the diagnostic subject.
183    subject: Ident,
184
185    /// Fields against which references are resolved.
186    fields: &'fields Fields,
187
188    /// Optional parsed format declaration.
189    format: Option<Format>,
190
191    /// Optional custom display formatter.
192    display: Option<Path>,
193
194    /// Optional ordinary source reference.
195    source: Option<FieldRef>,
196
197    /// Optional transparent source reference.
198    transparent: Option<Transparent>,
199
200    /// Whether automatic conversion was requested.
201    from: bool,
202}
203
204impl Declaration<'_> {
205    /// Check declaration compatibility and resolve field-dependent semantics
206    /// once.
207    fn validate(self) -> syn::Result<(Display, ErrorSource, Option<Conversion>)> {
208        let Self {
209            subject,
210            fields,
211            format,
212            display,
213            source,
214            transparent,
215            from,
216        } = self;
217
218        if format.is_some() && display.is_some() {
219            return Err(syn::Error::new_spanned(
220                subject,
221                "error cannot declare both a format string and `display(...)`",
222            ));
223        }
224
225        if transparent.is_some() && (format.is_some() || display.is_some()) {
226            return Err(syn::Error::new_spanned(
227                subject,
228                "transparent error cannot also declare display formatting",
229            ));
230        }
231
232        if transparent.is_some() && source.is_some() {
233            return Err(syn::Error::new_spanned(
234                subject,
235                "transparent error cannot also declare an ordinary source",
236            ));
237        }
238
239        if transparent.is_some() && from {
240            return Err(syn::Error::new_spanned(subject, "transparent error cannot also derive `From`"));
241        }
242
243        if let Some(Transparent(field_ref)) = transparent {
244            let field = field_ref.resolve(fields)?;
245            let sole = fields.sole()?;
246
247            if field != sole {
248                return Err(syn::Error::new_spanned(subject, "transparent error must target its sole field"));
249            }
250
251            let source = Source::new(fields, field);
252
253            if matches!(source.shape(), SourceShape::Optional | SourceShape::OptionalBoxed) {
254                return Err(syn::Error::new_spanned(subject, "transparent error source cannot be optional"));
255            }
256
257            let display = Display::Transparent(field);
258            let source = ErrorSource::Transparent(source);
259
260            return Ok((display, source, None));
261        }
262
263        let display = match (format, display) {
264            (Some(format), None) => Display::Format(format.resolve(fields)?),
265            (None, Some(path)) => Display::Custom(path),
266            (None, None) => {
267                return Err(syn::Error::new_spanned(
268                    subject,
269                    "error requires a format string, `display(...)`, or `transparent(...)`",
270                ));
271            }
272            (Some(_), Some(_)) => {
273                return Err(syn::Error::new_spanned(subject, "error has conflicting display declarations"));
274            }
275        };
276
277        let conversion = if from {
278            let field = fields.sole()?;
279            let field_type = fields.ty(field).clone();
280
281            Some(Conversion::new(field, field_type))
282        } else {
283            None
284        };
285
286        let source = match (source, conversion.as_ref()) {
287            (Some(field_ref), Some(conversion)) => {
288                let field = field_ref.resolve(fields)?;
289
290                if field != conversion.field() {
291                    return Err(syn::Error::new_spanned(
292                        subject,
293                        "`from` conversion and explicit source must refer to the same field",
294                    ));
295                }
296
297                ErrorSource::Field(Source::new(fields, field))
298            }
299            (Some(field_ref), None) => {
300                let field = field_ref.resolve(fields)?;
301
302                ErrorSource::Field(Source::new(fields, field))
303            }
304            (None, Some(conversion)) => ErrorSource::Field(Source::new(fields, conversion.field())),
305            (None, None) => ErrorSource::None,
306        };
307
308        Ok((display, source, conversion))
309    }
310}