| 1 | use crate::internals::ast::{Container, Data, Field, Style}; |
| 2 | use crate::internals::attr::{Default, Identifier, TagType}; |
| 3 | use crate::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. |
| 8 | pub 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. |
| 27 | fn 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 | // |
| 66 | fn check_remote_generic(cx: &Ctxt, cont: &Container) { |
| 67 | if let Some(remote: &Path) = cont.attrs.remote() { |
| 68 | let local_has_generic: bool = !cont.generics.params.is_empty(); |
| 69 | let remote_has_generic: bool = !remote.segments.last().unwrap().arguments.is_none(); |
| 70 | if local_has_generic && remote_has_generic { |
| 71 | cx.error_spanned_by(obj:remote, msg:"remove generic parameters from this path" ); |
| 72 | } |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | // Getters are only allowed inside structs (not enums) with the `remote` |
| 77 | // attribute. |
| 78 | fn 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 | obj:cont.original, |
| 84 | msg:"#[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 | obj:cont.original, |
| 92 | msg:"#[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. |
| 100 | fn check_flatten(cx: &Ctxt, cont: &Container) { |
| 101 | match &cont.data { |
| 102 | Data::Enum(variants: &Vec>) => { |
| 103 | for variant: &Variant<'_> in variants { |
| 104 | for field: &Field<'_> in &variant.fields { |
| 105 | check_flatten_field(cx, variant.style, field); |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | Data::Struct(style: &Style, fields: &Vec>) => { |
| 110 | for field: &Field<'_> in fields { |
| 111 | check_flatten_field(cx, *style, field); |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | fn 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 | obj:field.original, |
| 125 | msg:"#[serde(flatten)] cannot be used on tuple structs" , |
| 126 | ); |
| 127 | } |
| 128 | Style::Newtype => { |
| 129 | cx.error_spanned_by( |
| 130 | obj:field.original, |
| 131 | msg:"#[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. |
| 144 | fn 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. |
| 226 | fn 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. |
| 300 | fn 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.value == tag { |
| 333 | diagnose_conflict(); |
| 334 | return; |
| 335 | } |
| 336 | |
| 337 | for de_name in field.attrs.aliases() { |
| 338 | if check_de && de_name.value == 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. |
| 352 | fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) { |
| 353 | let (type_tag: &String, content_tag: &String) = match cont.attrs.tag() { |
| 354 | TagType::Adjacent { tag: &String, content: &String } => (tag, content), |
| 355 | TagType::Internal { .. } | TagType::External | TagType::None => return, |
| 356 | }; |
| 357 | |
| 358 | if type_tag == content_tag { |
| 359 | cx.error_spanned_by( |
| 360 | obj:cont.original, |
| 361 | msg: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. |
| 370 | fn 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 | |
| 448 | fn member_message(member: &Member) -> String { |
| 449 | match member { |
| 450 | Member::Named(ident: &Ident) => format!("` {}`" , ident), |
| 451 | Member::Unnamed(i: &Index) => format!("# {}" , i.index), |
| 452 | } |
| 453 | } |
| 454 | |
| 455 | fn allow_transparent(field: &Field, derive: Derive) -> bool { |
| 456 | if let Type::Path(ty: &TypePath) = ungroup(field.ty) { |
| 457 | if let Some(seg: &PathSegment) = 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 | |
| 470 | fn 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 | obj:cont.original, |
| 474 | msg:"#[serde(from = \"... \")] and #[serde(try_from = \"... \")] conflict with each other" , |
| 475 | ); |
| 476 | } |
| 477 | } |
| 478 | |