serde_derive/internals/
check.rs

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