• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::internals::ast::{Container, Data, Field, Style};
2 use crate::internals::attr::{Default, Identifier, TagType};
3 use crate::internals::{ungroup, Ctxt, Derive};
4 use 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.
check(cx: &Ctxt, cont: &mut Container, derive: Derive)8 pub 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.
check_default_on_tuple(cx: &Ctxt, cont: &Container)27 fn 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 //
check_remote_generic(cx: &Ctxt, cont: &Container)66 fn 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.
check_getter(cx: &Ctxt, cont: &Container)78 fn 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.
check_flatten(cx: &Ctxt, cont: &Container)100 fn 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 
check_flatten_field(cx: &Ctxt, style: Style, field: &Field)117 fn 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.
check_identifier(cx: &Ctxt, cont: &Container)144 fn 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.
check_variant_skip_attrs(cx: &Ctxt, cont: &Container)226 fn 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.
check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container)300 fn 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 == tag {
333                         diagnose_conflict();
334                         return;
335                     }
336 
337                     for de_name in field.attrs.aliases() {
338                         if check_de && de_name == 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.
check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container)352 fn 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.
check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive)370 fn 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 
member_message(member: &Member) -> String448 fn member_message(member: &Member) -> String {
449     match member {
450         Member::Named(ident) => format!("`{}`", ident),
451         Member::Unnamed(i) => format!("#{}", i.index),
452     }
453 }
454 
allow_transparent(field: &Field, derive: Derive) -> bool455 fn 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 
check_from_and_try_from(cx: &Ctxt, cont: &mut Container)470 fn 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 }
478