]> git.proxmox.com Git - rustc.git/blame - vendor/serde_derive/src/internals/check.rs
New upstream version 1.45.0+dfsg1
[rustc.git] / vendor / serde_derive / src / internals / check.rs
CommitLineData
8faf50e0 1use internals::ast::{Container, Data, Field, Style};
f9f354fc 2use internals::attr::{Identifier, TagType};
8faf50e0
XL
3use internals::{Ctxt, Derive};
4use syn::{Member, Type};
3b2f2976
XL
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.
8faf50e0 8pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) {
3b2f2976 9 check_getter(cx, cont);
0531ce1d 10 check_flatten(cx, cont);
3b2f2976 11 check_identifier(cx, cont);
ea8adc8c 12 check_variant_skip_attrs(cx, cont);
0531ce1d
XL
13 check_internal_tag_field_name_conflict(cx, cont);
14 check_adjacent_tag_conflict(cx, cont);
8faf50e0 15 check_transparent(cx, cont, derive);
f9f354fc 16 check_from_and_try_from(cx, cont);
3b2f2976
XL
17}
18
19/// Getters are only allowed inside structs (not enums) with the `remote`
20/// attribute.
21fn check_getter(cx: &Ctxt, cont: &Container) {
0531ce1d
XL
22 match cont.data {
23 Data::Enum(_) => {
24 if cont.data.has_getter() {
0731742a
XL
25 cx.error_spanned_by(
26 cont.original,
27 "#[serde(getter = \"...\")] is not allowed in an enum",
28 );
3b2f2976
XL
29 }
30 }
0531ce1d
XL
31 Data::Struct(_, _) => {
32 if cont.data.has_getter() && cont.attrs.remote().is_none() {
0731742a
XL
33 cx.error_spanned_by(
34 cont.original,
f9f354fc 35 "#[serde(getter = \"...\")] can only be used in structs that have #[serde(remote = \"...\")]",
3b2f2976
XL
36 );
37 }
38 }
39 }
40}
41
0531ce1d
XL
42/// Flattening has some restrictions we can test.
43fn check_flatten(cx: &Ctxt, cont: &Container) {
f9f354fc
XL
44 match &cont.data {
45 Data::Enum(variants) => {
8faf50e0
XL
46 for variant in variants {
47 for field in &variant.fields {
48 check_flatten_field(cx, variant.style, field);
49 }
83c7162d 50 }
0531ce1d 51 }
f9f354fc 52 Data::Struct(style, fields) => {
8faf50e0 53 for field in fields {
f9f354fc 54 check_flatten_field(cx, *style, field);
0531ce1d
XL
55 }
56 }
57 }
58}
59
8faf50e0
XL
60fn check_flatten_field(cx: &Ctxt, style: Style, field: &Field) {
61 if !field.attrs.flatten() {
62 return;
63 }
64 match style {
65 Style::Tuple => {
0731742a
XL
66 cx.error_spanned_by(
67 field.original,
68 "#[serde(flatten)] cannot be used on tuple structs",
69 );
8faf50e0
XL
70 }
71 Style::Newtype => {
0731742a
XL
72 cx.error_spanned_by(
73 field.original,
74 "#[serde(flatten)] cannot be used on newtype structs",
75 );
8faf50e0
XL
76 }
77 _ => {}
78 }
8faf50e0
XL
79}
80
3b2f2976 81/// The `other` attribute must be used at most once and it must be the last
0731742a 82/// variant of an enum.
3b2f2976
XL
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.
87fn check_identifier(cx: &Ctxt, cont: &Container) {
f9f354fc
XL
88 let variants = match &cont.data {
89 Data::Enum(variants) => variants,
0531ce1d 90 Data::Struct(_, _) => {
3b2f2976
XL
91 return;
92 }
93 };
94
95 for (i, variant) in variants.iter().enumerate() {
ff7c6d11
XL
96 match (
97 variant.style,
98 cont.attrs.identifier(),
99 variant.attrs.other(),
0731742a 100 cont.attrs.tag(),
ff7c6d11 101 ) {
0731742a
XL
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
f9f354fc 111 (_, Identifier::No, true, &TagType::None) => {
0731742a
XL
112 cx.error_spanned_by(
113 variant.original,
114 "#[serde(other)] cannot appear on untagged enum",
115 );
3b2f2976
XL
116 }
117
118 // Variant with `other` attribute must be the last one.
0731742a 119 (Style::Unit, Identifier::Field, true, _) | (Style::Unit, Identifier::No, true, _) => {
3b2f2976 120 if i < variants.len() - 1 {
0731742a
XL
121 cx.error_spanned_by(
122 variant.original,
123 "#[serde(other)] must be on the last variant",
124 );
3b2f2976
XL
125 }
126 }
127
128 // Variant with `other` attribute must be a unit variant.
0731742a
XL
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 );
3b2f2976
XL
134 }
135
136 // Any sort of variant is allowed if this is not an identifier.
0731742a 137 (_, Identifier::No, false, _) => {}
3b2f2976
XL
138
139 // Unit variant without `other` attribute is always fine.
0731742a 140 (Style::Unit, _, false, _) => {}
3b2f2976
XL
141
142 // The last field is allowed to be a newtype catch-all.
0731742a 143 (Style::Newtype, Identifier::Field, false, _) => {
3b2f2976 144 if i < variants.len() - 1 {
0731742a
XL
145 cx.error_spanned_by(
146 variant.original,
147 format!("`{}` must be the last variant", variant.ident),
148 );
3b2f2976
XL
149 }
150 }
151
0731742a
XL
152 (_, Identifier::Field, false, _) => {
153 cx.error_spanned_by(
154 variant.original,
155 "#[serde(field_identifier)] may only contain unit variants",
156 );
3b2f2976
XL
157 }
158
0731742a
XL
159 (_, Identifier::Variant, false, _) => {
160 cx.error_spanned_by(
161 variant.original,
162 "#[serde(variant_identifier)] may only contain unit variants",
163 );
3b2f2976
XL
164 }
165 }
166 }
167}
ea8adc8c
XL
168
169/// Skip-(de)serializing attributes are not allowed on variants marked
170/// (de)serialize_with.
171fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
f9f354fc
XL
172 let variants = match &cont.data {
173 Data::Enum(variants) => variants,
0531ce1d 174 Data::Struct(_, _) => {
ea8adc8c
XL
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() {
f9f354fc
XL
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 );
ea8adc8c
XL
189 }
190
8faf50e0
XL
191 for field in &variant.fields {
192 let member = member_message(&field.member);
ea8adc8c
XL
193
194 if field.attrs.skip_serializing() {
f9f354fc
XL
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 );
ea8adc8c
XL
202 }
203
204 if field.attrs.skip_serializing_if().is_some() {
f9f354fc
XL
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 );
ea8adc8c
XL
212 }
213 }
214 }
215
216 if variant.attrs.deserialize_with().is_some() {
217 if variant.attrs.skip_deserializing() {
f9f354fc
XL
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 );
ea8adc8c
XL
225 }
226
8faf50e0 227 for field in &variant.fields {
ea8adc8c 228 if field.attrs.skip_deserializing() {
8faf50e0 229 let member = member_message(&field.member);
ff7c6d11 230
f9f354fc
XL
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 );
ea8adc8c
XL
238 }
239 }
240 }
241 }
242}
0531ce1d
XL
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.
83c7162d 248fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
f9f354fc
XL
249 let variants = match &cont.data {
250 Data::Enum(variants) => variants,
0531ce1d
XL
251 Data::Struct(_, _) => return,
252 };
253
f9f354fc
XL
254 let tag = match cont.attrs.tag() {
255 TagType::Internal { tag } => tag.as_str(),
256 TagType::External | TagType::Adjacent { .. } | TagType::None => return,
0531ce1d
XL
257 };
258
f9f354fc
XL
259 let diagnose_conflict = || {
260 cx.error_spanned_by(
261 cont.original,
262 format!("variant field name `{}` conflicts with internal tag", tag),
263 )
264 };
0531ce1d
XL
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();
0531ce1d 274
f9f354fc 275 if check_ser && ser_name == tag {
0531ce1d
XL
276 diagnose_conflict();
277 return;
278 }
f9f354fc
XL
279
280 for de_name in field.attrs.aliases() {
281 if check_de && de_name == tag {
282 diagnose_conflict();
283 return;
284 }
285 }
0531ce1d 286 }
83c7162d
XL
287 }
288 Style::Unit | Style::Newtype | Style::Tuple => {}
0531ce1d
XL
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.
295fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
f9f354fc
XL
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,
0531ce1d
XL
299 };
300
301 if type_tag == content_tag {
f9f354fc
XL
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 );
0531ce1d
XL
309 }
310}
8faf50e0
XL
311
312/// Enums and unit structs cannot be transparent.
313fn 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() {
0731742a
XL
319 cx.error_spanned_by(
320 cont.original,
321 "#[serde(transparent)] is not allowed with #[serde(from = \"...\")]",
322 );
8faf50e0
XL
323 }
324
f9f354fc
XL
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
8faf50e0 332 if cont.attrs.type_into().is_some() {
0731742a
XL
333 cx.error_spanned_by(
334 cont.original,
335 "#[serde(transparent)] is not allowed with #[serde(into = \"...\")]",
336 );
8faf50e0
XL
337 }
338
f9f354fc 339 let fields = match &mut cont.data {
8faf50e0 340 Data::Enum(_) => {
0731742a
XL
341 cx.error_spanned_by(
342 cont.original,
343 "#[serde(transparent)] is not allowed on an enum",
344 );
8faf50e0
XL
345 return;
346 }
347 Data::Struct(Style::Unit, _) => {
0731742a
XL
348 cx.error_spanned_by(
349 cont.original,
350 "#[serde(transparent)] is not allowed on a unit struct",
351 );
8faf50e0
XL
352 return;
353 }
f9f354fc 354 Data::Struct(_, fields) => fields,
8faf50e0
XL
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() {
0731742a
XL
362 cx.error_spanned_by(
363 cont.original,
8faf50e0
XL
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 => {
0731742a
XL
376 cx.error_spanned_by(
377 cont.original,
378 "#[serde(transparent)] requires at least one field that is not skipped",
379 );
8faf50e0
XL
380 }
381 Derive::Deserialize => {
0731742a
XL
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 );
8faf50e0
XL
386 }
387 },
388 }
389}
390
391fn member_message(member: &Member) -> String {
f9f354fc
XL
392 match member {
393 Member::Named(ident) => format!("`{}`", ident),
394 Member::Unnamed(i) => format!("#{}", i.index),
8faf50e0
XL
395 }
396}
397
398fn allow_transparent(field: &Field, derive: Derive) -> bool {
f9f354fc 399 if let Type::Path(ty) = field.ty {
8faf50e0 400 if let Some(seg) = ty.path.segments.last() {
f9f354fc 401 if seg.ident == "PhantomData" {
8faf50e0
XL
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}
f9f354fc
XL
412
413fn 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}