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 == 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.
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}
478