serde_derive/internals/
check.rs

1use internals::ast::{Container, Data, Field, Style};
2use internals::attr::{Identifier, TagType};
3use internals::{Ctxt, Derive};
4use syn::{Member, Type};
5
6/// Cross-cutting checks that require looking at more than a single attrs
7/// object. Simpler checks should happen when parsing and building the attrs.
8pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) {
9    check_getter(cx, cont);
10    check_flatten(cx, cont);
11    check_identifier(cx, cont);
12    check_variant_skip_attrs(cx, cont);
13    check_internal_tag_field_name_conflict(cx, cont);
14    check_adjacent_tag_conflict(cx, cont);
15    check_transparent(cx, cont, derive);
16    check_from_and_try_from(cx, cont);
17}
18
19/// Getters are only allowed inside structs (not enums) with the `remote`
20/// attribute.
21fn check_getter(cx: &Ctxt, cont: &Container) {
22    match cont.data {
23        Data::Enum(_) => {
24            if cont.data.has_getter() {
25                cx.error_spanned_by(
26                    cont.original,
27                    "#[serde(getter = \"...\")] is not allowed in an enum",
28                );
29            }
30        }
31        Data::Struct(_, _) => {
32            if cont.data.has_getter() && cont.attrs.remote().is_none() {
33                cx.error_spanned_by(
34                    cont.original,
35                    "#[serde(getter = \"...\")] can only be used in structs that have #[serde(remote = \"...\")]",
36                );
37            }
38        }
39    }
40}
41
42/// Flattening has some restrictions we can test.
43fn check_flatten(cx: &Ctxt, cont: &Container) {
44    match &cont.data {
45        Data::Enum(variants) => {
46            for variant in variants {
47                for field in &variant.fields {
48                    check_flatten_field(cx, variant.style, field);
49                }
50            }
51        }
52        Data::Struct(style, fields) => {
53            for field in fields {
54                check_flatten_field(cx, *style, field);
55            }
56        }
57    }
58}
59
60fn check_flatten_field(cx: &Ctxt, style: Style, field: &Field) {
61    if !field.attrs.flatten() {
62        return;
63    }
64    match style {
65        Style::Tuple => {
66            cx.error_spanned_by(
67                field.original,
68                "#[serde(flatten)] cannot be used on tuple structs",
69            );
70        }
71        Style::Newtype => {
72            cx.error_spanned_by(
73                field.original,
74                "#[serde(flatten)] cannot be used on newtype structs",
75            );
76        }
77        _ => {}
78    }
79}
80
81/// The `other` attribute must be used at most once and it must be the last
82/// variant of an enum.
83///
84/// Inside a `variant_identifier` all variants must be unit variants. Inside a
85/// `field_identifier` all but possibly one variant must be unit variants. The
86/// last variant may be a newtype variant which is an implicit "other" case.
87fn check_identifier(cx: &Ctxt, cont: &Container) {
88    let variants = match &cont.data {
89        Data::Enum(variants) => variants,
90        Data::Struct(_, _) => {
91            return;
92        }
93    };
94
95    for (i, variant) in variants.iter().enumerate() {
96        match (
97            variant.style,
98            cont.attrs.identifier(),
99            variant.attrs.other(),
100            cont.attrs.tag(),
101        ) {
102            // The `other` attribute may not be used in a variant_identifier.
103            (_, Identifier::Variant, true, _) => {
104                cx.error_spanned_by(
105                    variant.original,
106                    "#[serde(other)] may not be used on a variant identifier",
107                );
108            }
109
110            // Variant with `other` attribute cannot appear in untagged enum
111            (_, Identifier::No, true, &TagType::None) => {
112                cx.error_spanned_by(
113                    variant.original,
114                    "#[serde(other)] cannot appear on untagged enum",
115                );
116            }
117
118            // Variant with `other` attribute must be the last one.
119            (Style::Unit, Identifier::Field, true, _) | (Style::Unit, Identifier::No, true, _) => {
120                if i < variants.len() - 1 {
121                    cx.error_spanned_by(
122                        variant.original,
123                        "#[serde(other)] must be on the last variant",
124                    );
125                }
126            }
127
128            // Variant with `other` attribute must be a unit variant.
129            (_, Identifier::Field, true, _) | (_, Identifier::No, true, _) => {
130                cx.error_spanned_by(
131                    variant.original,
132                    "#[serde(other)] must be on a unit variant",
133                );
134            }
135
136            // Any sort of variant is allowed if this is not an identifier.
137            (_, Identifier::No, false, _) => {}
138
139            // Unit variant without `other` attribute is always fine.
140            (Style::Unit, _, false, _) => {}
141
142            // The last field is allowed to be a newtype catch-all.
143            (Style::Newtype, Identifier::Field, false, _) => {
144                if i < variants.len() - 1 {
145                    cx.error_spanned_by(
146                        variant.original,
147                        format!("`{}` must be the last variant", variant.ident),
148                    );
149                }
150            }
151
152            (_, Identifier::Field, false, _) => {
153                cx.error_spanned_by(
154                    variant.original,
155                    "#[serde(field_identifier)] may only contain unit variants",
156                );
157            }
158
159            (_, Identifier::Variant, false, _) => {
160                cx.error_spanned_by(
161                    variant.original,
162                    "#[serde(variant_identifier)] may only contain unit variants",
163                );
164            }
165        }
166    }
167}
168
169/// Skip-(de)serializing attributes are not allowed on variants marked
170/// (de)serialize_with.
171fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
172    let variants = match &cont.data {
173        Data::Enum(variants) => variants,
174        Data::Struct(_, _) => {
175            return;
176        }
177    };
178
179    for variant in variants.iter() {
180        if variant.attrs.serialize_with().is_some() {
181            if variant.attrs.skip_serializing() {
182                cx.error_spanned_by(
183                    variant.original,
184                    format!(
185                        "variant `{}` cannot have both #[serde(serialize_with)] and #[serde(skip_serializing)]",
186                        variant.ident
187                    ),
188                );
189            }
190
191            for field in &variant.fields {
192                let member = member_message(&field.member);
193
194                if field.attrs.skip_serializing() {
195                    cx.error_spanned_by(
196                        variant.original,
197                        format!(
198                            "variant `{}` cannot have both #[serde(serialize_with)] and a field {} marked with #[serde(skip_serializing)]",
199                            variant.ident, member
200                        ),
201                    );
202                }
203
204                if field.attrs.skip_serializing_if().is_some() {
205                    cx.error_spanned_by(
206                        variant.original,
207                        format!(
208                            "variant `{}` cannot have both #[serde(serialize_with)] and a field {} marked with #[serde(skip_serializing_if)]",
209                            variant.ident, member
210                        ),
211                    );
212                }
213            }
214        }
215
216        if variant.attrs.deserialize_with().is_some() {
217            if variant.attrs.skip_deserializing() {
218                cx.error_spanned_by(
219                    variant.original,
220                    format!(
221                        "variant `{}` cannot have both #[serde(deserialize_with)] and #[serde(skip_deserializing)]",
222                        variant.ident
223                    ),
224                );
225            }
226
227            for field in &variant.fields {
228                if field.attrs.skip_deserializing() {
229                    let member = member_message(&field.member);
230
231                    cx.error_spanned_by(
232                        variant.original,
233                        format!(
234                            "variant `{}` cannot have both #[serde(deserialize_with)] and a field {} marked with #[serde(skip_deserializing)]",
235                            variant.ident, member
236                        ),
237                    );
238                }
239            }
240        }
241    }
242}
243
244/// The tag of an internally-tagged struct variant must not be
245/// the same as either one of its fields, as this would result in
246/// duplicate keys in the serialized output and/or ambiguity in
247/// the to-be-deserialized input.
248fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
249    let variants = match &cont.data {
250        Data::Enum(variants) => variants,
251        Data::Struct(_, _) => return,
252    };
253
254    let tag = match cont.attrs.tag() {
255        TagType::Internal { tag } => tag.as_str(),
256        TagType::External | TagType::Adjacent { .. } | TagType::None => return,
257    };
258
259    let diagnose_conflict = || {
260        cx.error_spanned_by(
261            cont.original,
262            format!("variant field name `{}` conflicts with internal tag", tag),
263        )
264    };
265
266    for variant in variants {
267        match variant.style {
268            Style::Struct => {
269                for field in &variant.fields {
270                    let check_ser = !field.attrs.skip_serializing();
271                    let check_de = !field.attrs.skip_deserializing();
272                    let name = field.attrs.name();
273                    let ser_name = name.serialize_name();
274
275                    if check_ser && ser_name == tag {
276                        diagnose_conflict();
277                        return;
278                    }
279
280                    for de_name in field.attrs.aliases() {
281                        if check_de && de_name == tag {
282                            diagnose_conflict();
283                            return;
284                        }
285                    }
286                }
287            }
288            Style::Unit | Style::Newtype | Style::Tuple => {}
289        }
290    }
291}
292
293/// In the case of adjacently-tagged enums, the type and the
294/// contents tag must differ, for the same reason.
295fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
296    let (type_tag, content_tag) = match cont.attrs.tag() {
297        TagType::Adjacent { tag, content } => (tag, content),
298        TagType::Internal { .. } | TagType::External | TagType::None => return,
299    };
300
301    if type_tag == content_tag {
302        cx.error_spanned_by(
303            cont.original,
304            format!(
305                "enum tags `{}` for type and content conflict with each other",
306                type_tag
307            ),
308        );
309    }
310}
311
312/// Enums and unit structs cannot be transparent.
313fn check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive) {
314    if !cont.attrs.transparent() {
315        return;
316    }
317
318    if cont.attrs.type_from().is_some() {
319        cx.error_spanned_by(
320            cont.original,
321            "#[serde(transparent)] is not allowed with #[serde(from = \"...\")]",
322        );
323    }
324
325    if cont.attrs.type_try_from().is_some() {
326        cx.error_spanned_by(
327            cont.original,
328            "#[serde(transparent)] is not allowed with #[serde(try_from = \"...\")]",
329        );
330    }
331
332    if cont.attrs.type_into().is_some() {
333        cx.error_spanned_by(
334            cont.original,
335            "#[serde(transparent)] is not allowed with #[serde(into = \"...\")]",
336        );
337    }
338
339    let fields = match &mut cont.data {
340        Data::Enum(_) => {
341            cx.error_spanned_by(
342                cont.original,
343                "#[serde(transparent)] is not allowed on an enum",
344            );
345            return;
346        }
347        Data::Struct(Style::Unit, _) => {
348            cx.error_spanned_by(
349                cont.original,
350                "#[serde(transparent)] is not allowed on a unit struct",
351            );
352            return;
353        }
354        Data::Struct(_, fields) => fields,
355    };
356
357    let mut transparent_field = None;
358
359    for field in fields {
360        if allow_transparent(field, derive) {
361            if transparent_field.is_some() {
362                cx.error_spanned_by(
363                    cont.original,
364                    "#[serde(transparent)] requires struct to have at most one transparent field",
365                );
366                return;
367            }
368            transparent_field = Some(field);
369        }
370    }
371
372    match transparent_field {
373        Some(transparent_field) => transparent_field.attrs.mark_transparent(),
374        None => match derive {
375            Derive::Serialize => {
376                cx.error_spanned_by(
377                    cont.original,
378                    "#[serde(transparent)] requires at least one field that is not skipped",
379                );
380            }
381            Derive::Deserialize => {
382                cx.error_spanned_by(
383                    cont.original,
384                    "#[serde(transparent)] requires at least one field that is neither skipped nor has a default",
385                );
386            }
387        },
388    }
389}
390
391fn member_message(member: &Member) -> String {
392    match member {
393        Member::Named(ident) => format!("`{}`", ident),
394        Member::Unnamed(i) => format!("#{}", i.index),
395    }
396}
397
398fn allow_transparent(field: &Field, derive: Derive) -> bool {
399    if let Type::Path(ty) = field.ty {
400        if let Some(seg) = ty.path.segments.last() {
401            if seg.ident == "PhantomData" {
402                return false;
403            }
404        }
405    }
406
407    match derive {
408        Derive::Serialize => !field.attrs.skip_serializing(),
409        Derive::Deserialize => !field.attrs.skip_deserializing() && field.attrs.default().is_none(),
410    }
411}
412
413fn check_from_and_try_from(cx: &Ctxt, cont: &mut Container) {
414    if cont.attrs.type_from().is_some() && cont.attrs.type_try_from().is_some() {
415        cx.error_spanned_by(
416            cont.original,
417            "#[serde(from = \"...\")] and #[serde(try_from = \"...\")] conflict with each other",
418        );
419    }
420}