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};
56// 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) {
9check_default_on_tuple(cx, cont);
10check_remote_generic(cx, cont);
11check_getter(cx, cont);
12check_flatten(cx, cont);
13check_identifier(cx, cont);
14check_variant_skip_attrs(cx, cont);
15check_internal_tag_field_name_conflict(cx, cont);
16check_adjacent_tag_conflict(cx, cont);
17check_transparent(cx, cont, derive);
18check_from_and_try_from(cx, cont);
19}
2021// 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) {
28if let Default::None = cont.attrs.default() {
29if let Data::Struct(Style::Tuple, fields) = &cont.data {
30let mut first_default_index = None;
31for (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.
34if field.attrs.skip_deserializing() {
35continue;
36 }
37if let Default::None = field.attrs.default() {
38if let Some(first) = first_default_index {
39 cx.error_spanned_by(
40 field.ty,
41::alloc::__export::must_use({
::alloc::fmt::format(format_args!("field must have #[serde(default)] because previous field {0} has #[serde(default)]",
first))
})format!("field must have #[serde(default)] because previous field {} has #[serde(default)]", first),
42 );
43 }
44continue;
45 }
46if first_default_index.is_none() {
47 first_default_index = Some(i);
48 }
49 }
50 }
51 }
52}
5354// 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) {
67if let Some(remote) = cont.attrs.remote() {
68let local_has_generic = !cont.generics.params.is_empty();
69let remote_has_generic = !remote.segments.last().unwrap().arguments.is_none();
70if local_has_generic && remote_has_generic {
71cx.error_spanned_by(remote, "remove generic parameters from this path");
72 }
73 }
74}
7576// Getters are only allowed inside structs (not enums) with the `remote`
77// attribute.
78fn check_getter(cx: &Ctxt, cont: &Container) {
79match cont.data {
80 Data::Enum(_) => {
81if cont.data.has_getter() {
82cx.error_spanned_by(
83cont.original,
84"#[serde(getter = \"...\")] is not allowed in an enum",
85 );
86 }
87 }
88 Data::Struct(_, _) => {
89if cont.data.has_getter() && cont.attrs.remote().is_none() {
90cx.error_spanned_by(
91cont.original,
92"#[serde(getter = \"...\")] can only be used in structs that have #[serde(remote = \"...\")]",
93 );
94 }
95 }
96 }
97}
9899// Flattening has some restrictions we can test.
100fn check_flatten(cx: &Ctxt, cont: &Container) {
101match &cont.data {
102 Data::Enum(variants) => {
103for variant in variants {
104for field in &variant.fields {
105 check_flatten_field(cx, variant.style, field);
106 }
107 }
108 }
109 Data::Struct(style, fields) => {
110for field in fields {
111 check_flatten_field(cx, *style, field);
112 }
113 }
114 }
115}
116117fn check_flatten_field(cx: &Ctxt, style: Style, field: &Field) {
118if !field.attrs.flatten() {
119return;
120 }
121match style {
122 Style::Tuple => {
123cx.error_spanned_by(
124field.original,
125"#[serde(flatten)] cannot be used on tuple structs",
126 );
127 }
128 Style::Newtype => {
129cx.error_spanned_by(
130field.original,
131"#[serde(flatten)] cannot be used on newtype structs",
132 );
133 }
134_ => {}
135 }
136}
137138// 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) {
145let variants = match &cont.data {
146 Data::Enum(variants) => variants,
147 Data::Struct(_, _) => return,
148 };
149150for (i, variant) in variants.iter().enumerate() {
151match (
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 }
164165// 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 }
172173// Variant with `other` attribute must be the last one.
174(Style::Unit, Identifier::Field, true, _) | (Style::Unit, Identifier::No, true, _) => {
175if i < variants.len() - 1 {
176 cx.error_spanned_by(
177 variant.original,
178"#[serde(other)] must be on the last variant",
179 );
180 }
181 }
182183// 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 }
190191// Any sort of variant is allowed if this is not an identifier.
192(_, Identifier::No, false, _) => {}
193194// Unit variant without `other` attribute is always fine.
195(Style::Unit, _, false, _) => {}
196197// The last field is allowed to be a newtype catch-all.
198(Style::Newtype, Identifier::Field, false, _) => {
199if i < variants.len() - 1 {
200 cx.error_spanned_by(
201 variant.original,
202::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` must be the last variant",
variant.ident))
})format!("`{}` must be the last variant", variant.ident),
203 );
204 }
205 }
206207 (_, Identifier::Field, false, _) => {
208 cx.error_spanned_by(
209 variant.original,
210"#[serde(field_identifier)] may only contain unit variants",
211 );
212 }
213214 (_, 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}
223224// Skip-(de)serializing attributes are not allowed on variants marked
225// (de)serialize_with.
226fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
227let variants = match &cont.data {
228 Data::Enum(variants) => variants,
229 Data::Struct(_, _) => return,
230 };
231232for variant in variants {
233if variant.attrs.serialize_with().is_some() {
234if variant.attrs.skip_serializing() {
235 cx.error_spanned_by(
236 variant.original,
237::alloc::__export::must_use({
::alloc::fmt::format(format_args!("variant `{0}` cannot have both #[serde(serialize_with)] and #[serde(skip_serializing)]",
variant.ident))
})format!(
238"variant `{}` cannot have both #[serde(serialize_with)] and #[serde(skip_serializing)]",
239 variant.ident
240 ),
241 );
242 }
243244for field in &variant.fields {
245let member = member_message(&field.member);
246247if field.attrs.skip_serializing() {
248 cx.error_spanned_by(
249 variant.original,
250::alloc::__export::must_use({
::alloc::fmt::format(format_args!("variant `{0}` cannot have both #[serde(serialize_with)] and a field {1} marked with #[serde(skip_serializing)]",
variant.ident, member))
})format!(
251"variant `{}` cannot have both #[serde(serialize_with)] and a field {} marked with #[serde(skip_serializing)]",
252 variant.ident, member
253 ),
254 );
255 }
256257if field.attrs.skip_serializing_if().is_some() {
258 cx.error_spanned_by(
259 variant.original,
260::alloc::__export::must_use({
::alloc::fmt::format(format_args!("variant `{0}` cannot have both #[serde(serialize_with)] and a field {1} marked with #[serde(skip_serializing_if)]",
variant.ident, member))
})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 }
268269if variant.attrs.deserialize_with().is_some() {
270if variant.attrs.skip_deserializing() {
271 cx.error_spanned_by(
272 variant.original,
273::alloc::__export::must_use({
::alloc::fmt::format(format_args!("variant `{0}` cannot have both #[serde(deserialize_with)] and #[serde(skip_deserializing)]",
variant.ident))
})format!(
274"variant `{}` cannot have both #[serde(deserialize_with)] and #[serde(skip_deserializing)]",
275 variant.ident
276 ),
277 );
278 }
279280for field in &variant.fields {
281if field.attrs.skip_deserializing() {
282let member = member_message(&field.member);
283284 cx.error_spanned_by(
285 variant.original,
286::alloc::__export::must_use({
::alloc::fmt::format(format_args!("variant `{0}` cannot have both #[serde(deserialize_with)] and a field {1} marked with #[serde(skip_deserializing)]",
variant.ident, member))
})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}
296297// 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) {
301let variants = match &cont.data {
302 Data::Enum(variants) => variants,
303 Data::Struct(_, _) => return,
304 };
305306let tag = match cont.attrs.tag() {
307 TagType::Internal { tag } => tag.as_str(),
308 TagType::External | TagType::Adjacent { .. } | TagType::None => return,
309 };
310311let diagnose_conflict = || {
312cx.error_spanned_by(
313cont.original,
314::alloc::__export::must_use({
::alloc::fmt::format(format_args!("variant field name `{0}` conflicts with internal tag",
tag))
})format!("variant field name `{}` conflicts with internal tag", tag),
315 );
316 };
317318for variant in variants {
319match variant.style {
320 Style::Struct => {
321if variant.attrs.untagged() {
322continue;
323 }
324for field in &variant.fields {
325let check_ser =
326 !(field.attrs.skip_serializing() || variant.attrs.skip_serializing());
327let check_de =
328 !(field.attrs.skip_deserializing() || variant.attrs.skip_deserializing());
329let name = field.attrs.name();
330let ser_name = name.serialize_name();
331332if check_ser && ser_name.value == tag {
333 diagnose_conflict();
334return;
335 }
336337for de_name in field.attrs.aliases() {
338if check_de && de_name.value == tag {
339 diagnose_conflict();
340return;
341 }
342 }
343 }
344 }
345 Style::Unit | Style::Newtype | Style::Tuple => {}
346 }
347 }
348}
349350// 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) {
353let (type_tag, content_tag) = match cont.attrs.tag() {
354 TagType::Adjacent { tag, content } => (tag, content),
355 TagType::Internal { .. } | TagType::External | TagType::None => return,
356 };
357358if type_tag == content_tag {
359cx.error_spanned_by(
360cont.original,
361::alloc::__export::must_use({
::alloc::fmt::format(format_args!("enum tags `{0}` for type and content conflict with each other",
type_tag))
})format!(
362"enum tags `{}` for type and content conflict with each other",
363 type_tag
364 ),
365 );
366 }
367}
368369// Enums and unit structs cannot be transparent.
370fn check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive) {
371if !cont.attrs.transparent() {
372return;
373 }
374375if cont.attrs.type_from().is_some() {
376cx.error_spanned_by(
377cont.original,
378"#[serde(transparent)] is not allowed with #[serde(from = \"...\")]",
379 );
380 }
381382if cont.attrs.type_try_from().is_some() {
383cx.error_spanned_by(
384cont.original,
385"#[serde(transparent)] is not allowed with #[serde(try_from = \"...\")]",
386 );
387 }
388389if cont.attrs.type_into().is_some() {
390cx.error_spanned_by(
391cont.original,
392"#[serde(transparent)] is not allowed with #[serde(into = \"...\")]",
393 );
394 }
395396let fields = match &mut cont.data {
397 Data::Enum(_) => {
398cx.error_spanned_by(
399cont.original,
400"#[serde(transparent)] is not allowed on an enum",
401 );
402return;
403 }
404 Data::Struct(Style::Unit, _) => {
405cx.error_spanned_by(
406cont.original,
407"#[serde(transparent)] is not allowed on a unit struct",
408 );
409return;
410 }
411 Data::Struct(_, fields) => fields,
412 };
413414let mut transparent_field = None;
415416for field in fields {
417if allow_transparent(field, derive) {
418if 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 );
423return;
424 }
425 transparent_field = Some(field);
426 }
427 }
428429match transparent_field {
430Some(transparent_field) => transparent_field.attrs.mark_transparent(),
431None => match derive {
432 Derive::Serialize => {
433cx.error_spanned_by(
434cont.original,
435"#[serde(transparent)] requires at least one field that is not skipped",
436 );
437 }
438 Derive::Deserialize => {
439cx.error_spanned_by(
440cont.original,
441"#[serde(transparent)] requires at least one field that is neither skipped nor has a default",
442 );
443 }
444 },
445 }
446}
447448fn member_message(member: &Member) -> String {
449match member {
450 Member::Named(ident) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`", ident))
})format!("`{}`", ident),
451 Member::Unnamed(i) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#{0}", i.index))
})format!("#{}", i.index),
452 }
453}
454455fn allow_transparent(field: &Field, derive: Derive) -> bool {
456if let Type::Path(ty) = ungroup(field.ty) {
457if let Some(seg) = ty.path.segments.last() {
458if seg.ident == "PhantomData" {
459return false;
460 }
461 }
462 }
463464match derive {
465 Derive::Serialize => !field.attrs.skip_serializing(),
466 Derive::Deserialize => !field.attrs.skip_deserializing() && field.attrs.default().is_none(),
467 }
468}
469470fn check_from_and_try_from(cx: &Ctxt, cont: &mut Container) {
471if cont.attrs.type_from().is_some() && cont.attrs.type_try_from().is_some() {
472cx.error_spanned_by(
473cont.original,
474"#[serde(from = \"...\")] and #[serde(try_from = \"...\")] conflict with each other",
475 );
476 }
477}