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