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