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