]> git.proxmox.com Git - rustc.git/blame - vendor/serde_derive/src/internals/check.rs
New upstream version 1.69.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};
f035d41b 3use internals::{ungroup, Ctxt, Derive};
8faf50e0 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) {
9ffffee4 9 check_remote_generic(cx, cont);
3b2f2976 10 check_getter(cx, cont);
0531ce1d 11 check_flatten(cx, cont);
3b2f2976 12 check_identifier(cx, cont);
ea8adc8c 13 check_variant_skip_attrs(cx, cont);
0531ce1d
XL
14 check_internal_tag_field_name_conflict(cx, cont);
15 check_adjacent_tag_conflict(cx, cont);
8faf50e0 16 check_transparent(cx, cont, derive);
f9f354fc 17 check_from_and_try_from(cx, cont);
3b2f2976
XL
18}
19
9ffffee4
FG
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///
32fn 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
3b2f2976
XL
42/// Getters are only allowed inside structs (not enums) with the `remote`
43/// attribute.
44fn check_getter(cx: &Ctxt, cont: &Container) {
0531ce1d
XL
45 match cont.data {
46 Data::Enum(_) => {
47 if cont.data.has_getter() {
0731742a
XL
48 cx.error_spanned_by(
49 cont.original,
50 "#[serde(getter = \"...\")] is not allowed in an enum",
51 );
3b2f2976
XL
52 }
53 }
0531ce1d
XL
54 Data::Struct(_, _) => {
55 if cont.data.has_getter() && cont.attrs.remote().is_none() {
0731742a
XL
56 cx.error_spanned_by(
57 cont.original,
f9f354fc 58 "#[serde(getter = \"...\")] can only be used in structs that have #[serde(remote = \"...\")]",
3b2f2976
XL
59 );
60 }
61 }
62 }
63}
64
0531ce1d
XL
65/// Flattening has some restrictions we can test.
66fn check_flatten(cx: &Ctxt, cont: &Container) {
f9f354fc
XL
67 match &cont.data {
68 Data::Enum(variants) => {
8faf50e0
XL
69 for variant in variants {
70 for field in &variant.fields {
71 check_flatten_field(cx, variant.style, field);
72 }
83c7162d 73 }
0531ce1d 74 }
f9f354fc 75 Data::Struct(style, fields) => {
8faf50e0 76 for field in fields {
f9f354fc 77 check_flatten_field(cx, *style, field);
0531ce1d
XL
78 }
79 }
80 }
81}
82
8faf50e0
XL
83fn check_flatten_field(cx: &Ctxt, style: Style, field: &Field) {
84 if !field.attrs.flatten() {
85 return;
86 }
87 match style {
88 Style::Tuple => {
0731742a
XL
89 cx.error_spanned_by(
90 field.original,
91 "#[serde(flatten)] cannot be used on tuple structs",
92 );
8faf50e0
XL
93 }
94 Style::Newtype => {
0731742a
XL
95 cx.error_spanned_by(
96 field.original,
97 "#[serde(flatten)] cannot be used on newtype structs",
98 );
8faf50e0
XL
99 }
100 _ => {}
101 }
8faf50e0
XL
102}
103
3b2f2976 104/// The `other` attribute must be used at most once and it must be the last
0731742a 105/// variant of an enum.
3b2f2976
XL
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.
110fn check_identifier(cx: &Ctxt, cont: &Container) {
f9f354fc
XL
111 let variants = match &cont.data {
112 Data::Enum(variants) => variants,
0531ce1d 113 Data::Struct(_, _) => {
3b2f2976
XL
114 return;
115 }
116 };
117
118 for (i, variant) in variants.iter().enumerate() {
ff7c6d11
XL
119 match (
120 variant.style,
121 cont.attrs.identifier(),
122 variant.attrs.other(),
0731742a 123 cont.attrs.tag(),
ff7c6d11 124 ) {
0731742a
XL
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
f9f354fc 134 (_, Identifier::No, true, &TagType::None) => {
0731742a
XL
135 cx.error_spanned_by(
136 variant.original,
137 "#[serde(other)] cannot appear on untagged enum",
138 );
3b2f2976
XL
139 }
140
141 // Variant with `other` attribute must be the last one.
0731742a 142 (Style::Unit, Identifier::Field, true, _) | (Style::Unit, Identifier::No, true, _) => {
3b2f2976 143 if i < variants.len() - 1 {
0731742a
XL
144 cx.error_spanned_by(
145 variant.original,
146 "#[serde(other)] must be on the last variant",
147 );
3b2f2976
XL
148 }
149 }
150
151 // Variant with `other` attribute must be a unit variant.
0731742a
XL
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 );
3b2f2976
XL
157 }
158
159 // Any sort of variant is allowed if this is not an identifier.
0731742a 160 (_, Identifier::No, false, _) => {}
3b2f2976
XL
161
162 // Unit variant without `other` attribute is always fine.
0731742a 163 (Style::Unit, _, false, _) => {}
3b2f2976
XL
164
165 // The last field is allowed to be a newtype catch-all.
0731742a 166 (Style::Newtype, Identifier::Field, false, _) => {
3b2f2976 167 if i < variants.len() - 1 {
0731742a
XL
168 cx.error_spanned_by(
169 variant.original,
170 format!("`{}` must be the last variant", variant.ident),
171 );
3b2f2976
XL
172 }
173 }
174
0731742a
XL
175 (_, Identifier::Field, false, _) => {
176 cx.error_spanned_by(
177 variant.original,
178 "#[serde(field_identifier)] may only contain unit variants",
179 );
3b2f2976
XL
180 }
181
0731742a
XL
182 (_, Identifier::Variant, false, _) => {
183 cx.error_spanned_by(
184 variant.original,
185 "#[serde(variant_identifier)] may only contain unit variants",
186 );
3b2f2976
XL
187 }
188 }
189 }
190}
ea8adc8c
XL
191
192/// Skip-(de)serializing attributes are not allowed on variants marked
193/// (de)serialize_with.
194fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
f9f354fc
XL
195 let variants = match &cont.data {
196 Data::Enum(variants) => variants,
0531ce1d 197 Data::Struct(_, _) => {
ea8adc8c
XL
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() {
f9f354fc
XL
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 );
ea8adc8c
XL
212 }
213
8faf50e0
XL
214 for field in &variant.fields {
215 let member = member_message(&field.member);
ea8adc8c
XL
216
217 if field.attrs.skip_serializing() {
f9f354fc
XL
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 );
ea8adc8c
XL
225 }
226
227 if field.attrs.skip_serializing_if().is_some() {
f9f354fc
XL
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 );
ea8adc8c
XL
235 }
236 }
237 }
238
239 if variant.attrs.deserialize_with().is_some() {
240 if variant.attrs.skip_deserializing() {
f9f354fc
XL
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 );
ea8adc8c
XL
248 }
249
8faf50e0 250 for field in &variant.fields {
ea8adc8c 251 if field.attrs.skip_deserializing() {
8faf50e0 252 let member = member_message(&field.member);
ff7c6d11 253
f9f354fc
XL
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 );
ea8adc8c
XL
261 }
262 }
263 }
264 }
265}
0531ce1d
XL
266
267/// The tag of an internally-tagged struct variant must not be
268/// the same as either one of its fields, as this would result in
269/// duplicate keys in the serialized output and/or ambiguity in
270/// the to-be-deserialized input.
83c7162d 271fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
f9f354fc
XL
272 let variants = match &cont.data {
273 Data::Enum(variants) => variants,
0531ce1d
XL
274 Data::Struct(_, _) => return,
275 };
276
f9f354fc
XL
277 let tag = match cont.attrs.tag() {
278 TagType::Internal { tag } => tag.as_str(),
279 TagType::External | TagType::Adjacent { .. } | TagType::None => return,
0531ce1d
XL
280 };
281
f9f354fc
XL
282 let diagnose_conflict = || {
283 cx.error_spanned_by(
284 cont.original,
285 format!("variant field name `{}` conflicts with internal tag", tag),
94222f64 286 );
f9f354fc 287 };
0531ce1d
XL
288
289 for variant in variants {
290 match variant.style {
291 Style::Struct => {
292 for field in &variant.fields {
293 let check_ser = !field.attrs.skip_serializing();
294 let check_de = !field.attrs.skip_deserializing();
295 let name = field.attrs.name();
296 let ser_name = name.serialize_name();
0531ce1d 297
f9f354fc 298 if check_ser && ser_name == tag {
0531ce1d
XL
299 diagnose_conflict();
300 return;
301 }
f9f354fc
XL
302
303 for de_name in field.attrs.aliases() {
304 if check_de && de_name == tag {
305 diagnose_conflict();
306 return;
307 }
308 }
0531ce1d 309 }
83c7162d
XL
310 }
311 Style::Unit | Style::Newtype | Style::Tuple => {}
0531ce1d
XL
312 }
313 }
314}
315
316/// In the case of adjacently-tagged enums, the type and the
317/// contents tag must differ, for the same reason.
318fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
f9f354fc
XL
319 let (type_tag, content_tag) = match cont.attrs.tag() {
320 TagType::Adjacent { tag, content } => (tag, content),
321 TagType::Internal { .. } | TagType::External | TagType::None => return,
0531ce1d
XL
322 };
323
324 if type_tag == content_tag {
f9f354fc
XL
325 cx.error_spanned_by(
326 cont.original,
327 format!(
328 "enum tags `{}` for type and content conflict with each other",
329 type_tag
330 ),
331 );
0531ce1d
XL
332 }
333}
8faf50e0
XL
334
335/// Enums and unit structs cannot be transparent.
336fn check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive) {
337 if !cont.attrs.transparent() {
338 return;
339 }
340
341 if cont.attrs.type_from().is_some() {
0731742a
XL
342 cx.error_spanned_by(
343 cont.original,
344 "#[serde(transparent)] is not allowed with #[serde(from = \"...\")]",
345 );
8faf50e0
XL
346 }
347
f9f354fc
XL
348 if cont.attrs.type_try_from().is_some() {
349 cx.error_spanned_by(
350 cont.original,
351 "#[serde(transparent)] is not allowed with #[serde(try_from = \"...\")]",
352 );
353 }
354
8faf50e0 355 if cont.attrs.type_into().is_some() {
0731742a
XL
356 cx.error_spanned_by(
357 cont.original,
358 "#[serde(transparent)] is not allowed with #[serde(into = \"...\")]",
359 );
8faf50e0
XL
360 }
361
f9f354fc 362 let fields = match &mut cont.data {
8faf50e0 363 Data::Enum(_) => {
0731742a
XL
364 cx.error_spanned_by(
365 cont.original,
366 "#[serde(transparent)] is not allowed on an enum",
367 );
8faf50e0
XL
368 return;
369 }
370 Data::Struct(Style::Unit, _) => {
0731742a
XL
371 cx.error_spanned_by(
372 cont.original,
373 "#[serde(transparent)] is not allowed on a unit struct",
374 );
8faf50e0
XL
375 return;
376 }
f9f354fc 377 Data::Struct(_, fields) => fields,
8faf50e0
XL
378 };
379
380 let mut transparent_field = None;
381
382 for field in fields {
383 if allow_transparent(field, derive) {
384 if transparent_field.is_some() {
0731742a
XL
385 cx.error_spanned_by(
386 cont.original,
8faf50e0
XL
387 "#[serde(transparent)] requires struct to have at most one transparent field",
388 );
389 return;
390 }
391 transparent_field = Some(field);
392 }
393 }
394
395 match transparent_field {
396 Some(transparent_field) => transparent_field.attrs.mark_transparent(),
397 None => match derive {
398 Derive::Serialize => {
0731742a
XL
399 cx.error_spanned_by(
400 cont.original,
401 "#[serde(transparent)] requires at least one field that is not skipped",
402 );
8faf50e0
XL
403 }
404 Derive::Deserialize => {
0731742a
XL
405 cx.error_spanned_by(
406 cont.original,
407 "#[serde(transparent)] requires at least one field that is neither skipped nor has a default",
408 );
8faf50e0
XL
409 }
410 },
411 }
412}
413
414fn member_message(member: &Member) -> String {
f9f354fc
XL
415 match member {
416 Member::Named(ident) => format!("`{}`", ident),
417 Member::Unnamed(i) => format!("#{}", i.index),
8faf50e0
XL
418 }
419}
420
421fn allow_transparent(field: &Field, derive: Derive) -> bool {
94222f64 422 if let Type::Path(ty) = ungroup(field.ty) {
8faf50e0 423 if let Some(seg) = ty.path.segments.last() {
f9f354fc 424 if seg.ident == "PhantomData" {
8faf50e0
XL
425 return false;
426 }
427 }
428 }
429
430 match derive {
431 Derive::Serialize => !field.attrs.skip_serializing(),
432 Derive::Deserialize => !field.attrs.skip_deserializing() && field.attrs.default().is_none(),
433 }
434}
f9f354fc
XL
435
436fn check_from_and_try_from(cx: &Ctxt, cont: &mut Container) {
437 if cont.attrs.type_from().is_some() && cont.attrs.type_try_from().is_some() {
438 cx.error_spanned_by(
439 cont.original,
440 "#[serde(from = \"...\")] and #[serde(try_from = \"...\")] conflict with each other",
441 );
442 }
443}