Skip to content

Commit

Permalink
Keep track of a span for alias strings
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Nov 11, 2024
1 parent f0b5c4f commit 373edcd
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 45 deletions.
5 changes: 3 additions & 2 deletions serde_derive/src/de.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::fragment::{Expr, Fragment, Match, Stmts};
use crate::internals::ast::{Container, Data, Field, Style, Variant};
use crate::internals::name::Name;
use crate::internals::{attr, replace_receiver, ungroup, Ctxt, Derive};
use crate::{bound, dummy, pretend, this};
use proc_macro2::{Literal, Span, TokenStream};
Expand Down Expand Up @@ -2002,7 +2003,7 @@ fn deserialize_untagged_newtype_variant(

struct FieldWithAliases<'a> {
ident: Ident,
aliases: &'a BTreeSet<String>,
aliases: &'a BTreeSet<Name>,
}

fn deserialize_generated_identifier(
Expand Down Expand Up @@ -2224,7 +2225,7 @@ fn deserialize_identifier(
let aliases = field
.aliases
.iter()
.map(|alias| Literal::byte_string(alias.as_bytes()));
.map(|alias| Literal::byte_string(alias.value.as_bytes()));
quote!(#(#aliases)|* => _serde::__private::Ok(#this_value::#ident))
});

Expand Down
61 changes: 37 additions & 24 deletions serde_derive/src/internals/attr.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::internals::name::MultiName;
use crate::internals::name::{MultiName, Name};
use crate::internals::symbol::*;
use crate::internals::{ungroup, Ctxt};
use proc_macro2::{Spacing, Span, TokenStream, TokenTree};
Expand Down Expand Up @@ -131,8 +131,8 @@ impl<'c, T> VecAttr<'c, T> {
}
}

fn unraw(ident: &Ident) -> String {
ident.to_string().trim_start_matches("r#").to_owned()
fn unraw(ident: &Ident) -> Ident {
Ident::new(ident.to_string().trim_start_matches("r#"), ident.span())
}

#[derive(Copy, Clone)]
Expand Down Expand Up @@ -278,8 +278,8 @@ impl Container {
// #[serde(rename = "foo")]
// #[serde(rename(serialize = "foo", deserialize = "bar"))]
let (ser, de) = get_renames(cx, RENAME, &meta)?;
ser_name.set_opt(&meta.path, ser.as_ref().map(syn::LitStr::value));
de_name.set_opt(&meta.path, de.as_ref().map(syn::LitStr::value));
ser_name.set_opt(&meta.path, ser.as_ref().map(Name::from));
de_name.set_opt(&meta.path, de.as_ref().map(Name::from));
} else if meta.path == RENAME_ALL {
// #[serde(rename_all = "foo")]
// #[serde(rename_all(serialize = "foo", deserialize = "bar"))]
Expand Down Expand Up @@ -518,7 +518,7 @@ impl Container {
}

Container {
name: MultiName::from_attrs(unraw(&item.ident), ser_name, de_name, None),
name: MultiName::from_attrs(Name::from(&unraw(&item.ident)), ser_name, de_name, None),
transparent: transparent.get(),
deny_unknown_fields: deny_unknown_fields.get(),
default: default.get().unwrap_or(Default::None),
Expand Down Expand Up @@ -783,15 +783,15 @@ impl Variant {
// #[serde(rename = "foo")]
// #[serde(rename(serialize = "foo", deserialize = "bar"))]
let (ser, de) = get_multiple_renames(cx, &meta)?;
ser_name.set_opt(&meta.path, ser.as_ref().map(syn::LitStr::value));
ser_name.set_opt(&meta.path, ser.as_ref().map(Name::from));
for de_value in de {
de_name.set_if_none(de_value.value());
de_aliases.insert(&meta.path, de_value.value());
de_name.set_if_none(Name::from(&de_value));
de_aliases.insert(&meta.path, Name::from(&de_value));
}
} else if meta.path == ALIAS {
// #[serde(alias = "foo")]
if let Some(s) = get_lit_str(cx, ALIAS, &meta)? {
de_aliases.insert(&meta.path, s.value());
de_aliases.insert(&meta.path, Name::from(&s));
}
} else if meta.path == RENAME_ALL {
// #[serde(rename_all = "foo")]
Expand Down Expand Up @@ -898,7 +898,12 @@ impl Variant {
}

Variant {
name: MultiName::from_attrs(unraw(&variant.ident), ser_name, de_name, Some(de_aliases)),
name: MultiName::from_attrs(
Name::from(&unraw(&variant.ident)),
ser_name,
de_name,
Some(de_aliases),
),
rename_all_rules: RenameAllRules {
serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None),
deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None),
Expand All @@ -919,16 +924,19 @@ impl Variant {
&self.name
}

pub fn aliases(&self) -> &BTreeSet<String> {
pub fn aliases(&self) -> &BTreeSet<Name> {
self.name.deserialize_aliases()
}

pub fn rename_by_rules(&mut self, rules: RenameAllRules) {
if !self.name.serialize_renamed {
self.name.serialize = rules.serialize.apply_to_variant(&self.name.serialize);
self.name.serialize.value =
rules.serialize.apply_to_variant(&self.name.serialize.value);
}
if !self.name.deserialize_renamed {
self.name.deserialize = rules.deserialize.apply_to_variant(&self.name.deserialize);
self.name.deserialize.value = rules
.deserialize
.apply_to_variant(&self.name.deserialize.value);
}
self.name
.deserialize_aliases
Expand Down Expand Up @@ -1033,8 +1041,11 @@ impl Field {
let mut flatten = BoolAttr::none(cx, FLATTEN);

let ident = match &field.ident {
Some(ident) => unraw(ident),
None => index.to_string(),
Some(ident) => Name::from(&unraw(ident)),
None => Name {
value: index.to_string(),
span: Span::call_site(),
},
};

if let Some(borrow_attribute) = attrs.and_then(|variant| variant.borrow.as_ref()) {
Expand Down Expand Up @@ -1070,15 +1081,15 @@ impl Field {
// #[serde(rename = "foo")]
// #[serde(rename(serialize = "foo", deserialize = "bar"))]
let (ser, de) = get_multiple_renames(cx, &meta)?;
ser_name.set_opt(&meta.path, ser.as_ref().map(syn::LitStr::value));
ser_name.set_opt(&meta.path, ser.as_ref().map(Name::from));
for de_value in de {
de_name.set_if_none(de_value.value());
de_aliases.insert(&meta.path, de_value.value());
de_name.set_if_none(Name::from(&de_value));
de_aliases.insert(&meta.path, Name::from(&de_value));
}
} else if meta.path == ALIAS {
// #[serde(alias = "foo")]
if let Some(s) = get_lit_str(cx, ALIAS, &meta)? {
de_aliases.insert(&meta.path, s.value());
de_aliases.insert(&meta.path, Name::from(&s));
}
} else if meta.path == DEFAULT {
if meta.input.peek(Token![=]) {
Expand Down Expand Up @@ -1261,16 +1272,18 @@ impl Field {
&self.name
}

pub fn aliases(&self) -> &BTreeSet<String> {
pub fn aliases(&self) -> &BTreeSet<Name> {
self.name.deserialize_aliases()
}

pub fn rename_by_rules(&mut self, rules: RenameAllRules) {
if !self.name.serialize_renamed {
self.name.serialize = rules.serialize.apply_to_field(&self.name.serialize);
self.name.serialize.value = rules.serialize.apply_to_field(&self.name.serialize.value);
}
if !self.name.deserialize_renamed {
self.name.deserialize = rules.deserialize.apply_to_field(&self.name.deserialize);
self.name.deserialize.value = rules
.deserialize
.apply_to_field(&self.name.deserialize.value);
}
self.name
.deserialize_aliases
Expand Down Expand Up @@ -1720,7 +1733,7 @@ fn is_primitive_path(path: &syn::Path, primitive: &str) -> bool {
// attribute on the field so there must be at least one borrowable lifetime.
fn borrowable_lifetimes(
cx: &Ctxt,
name: &str,
name: &Name,
field: &syn::Field,
) -> Result<BTreeSet<syn::Lifetime>, ()> {
let mut lifetimes = BTreeSet::new();
Expand Down
4 changes: 2 additions & 2 deletions serde_derive/src/internals/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,13 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
let name = field.attrs.name();
let ser_name = name.serialize_name();

if check_ser && ser_name == tag {
if check_ser && ser_name.value == tag {
diagnose_conflict();
return;
}

for de_name in field.attrs.aliases() {
if check_de && de_name == tag {
if check_de && de_name.value == tag {
diagnose_conflict();
return;
}
Expand Down
2 changes: 1 addition & 1 deletion serde_derive/src/internals/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
pub mod ast;
pub mod attr;
pub mod name;

mod case;
mod check;
mod ctxt;
mod name;
mod receiver;
mod respan;
mod symbol;
Expand Down
81 changes: 71 additions & 10 deletions serde_derive/src/internals/name.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
use crate::internals::attr::{Attr, VecAttr};
use proc_macro2::{Ident, Span, TokenStream};
use quote::ToTokens;
use std::cmp::Ordering;
use std::collections::BTreeSet;
use std::fmt::{self, Display};
use syn::LitStr;

pub struct MultiName {
pub(crate) serialize: String,
pub(crate) serialize: Name,
pub(crate) serialize_renamed: bool,
pub(crate) deserialize: String,
pub(crate) deserialize: Name,
pub(crate) deserialize_renamed: bool,
pub(crate) deserialize_aliases: BTreeSet<String>,
pub(crate) deserialize_aliases: BTreeSet<Name>,
}

impl MultiName {
pub(crate) fn from_attrs(
source_name: String,
ser_name: Attr<String>,
de_name: Attr<String>,
de_aliases: Option<VecAttr<String>>,
source_name: Name,
ser_name: Attr<Name>,
de_name: Attr<Name>,
de_aliases: Option<VecAttr<Name>>,
) -> Self {
let mut alias_set = BTreeSet::new();
if let Some(de_aliases) = de_aliases {
Expand All @@ -37,16 +42,72 @@ impl MultiName {
}

/// Return the container name for the container when serializing.
pub fn serialize_name(&self) -> &str {
pub fn serialize_name(&self) -> &Name {
&self.serialize
}

/// Return the container name for the container when deserializing.
pub fn deserialize_name(&self) -> &str {
pub fn deserialize_name(&self) -> &Name {
&self.deserialize
}

pub(crate) fn deserialize_aliases(&self) -> &BTreeSet<String> {
pub(crate) fn deserialize_aliases(&self) -> &BTreeSet<Name> {
&self.deserialize_aliases
}
}

#[derive(Clone)]
pub struct Name {
pub value: String,
pub span: Span,
}

impl ToTokens for Name {
fn to_tokens(&self, tokens: &mut TokenStream) {
LitStr::new(&self.value, self.span).to_tokens(tokens);
}
}

impl Ord for Name {
fn cmp(&self, other: &Self) -> Ordering {
Ord::cmp(&self.value, &other.value)
}
}

impl PartialOrd for Name {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(Ord::cmp(self, other))
}
}

impl Eq for Name {}

impl PartialEq for Name {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}

impl From<&Ident> for Name {
fn from(ident: &Ident) -> Self {
Name {
value: ident.to_string(),
span: ident.span(),
}
}
}

impl From<&LitStr> for Name {
fn from(lit: &LitStr) -> Self {
Name {
value: lit.value(),
span: lit.span(),
}
}
}

impl Display for Name {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.value, formatter)
}
}
13 changes: 7 additions & 6 deletions serde_derive/src/ser.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::fragment::{Fragment, Match, Stmts};
use crate::internals::ast::{Container, Data, Field, Style, Variant};
use crate::internals::name::Name;
use crate::internals::{attr, replace_receiver, Ctxt, Derive};
use crate::{bound, dummy, pretend, this};
use proc_macro2::{Span, TokenStream};
Expand Down Expand Up @@ -798,9 +799,9 @@ fn serialize_untagged_variant(

enum TupleVariant<'a> {
ExternallyTagged {
type_name: &'a str,
type_name: &'a Name,
variant_index: u32,
variant_name: &'a str,
variant_name: &'a Name,
},
Untagged,
}
Expand Down Expand Up @@ -867,11 +868,11 @@ fn serialize_tuple_variant(
enum StructVariant<'a> {
ExternallyTagged {
variant_index: u32,
variant_name: &'a str,
variant_name: &'a Name,
},
InternallyTagged {
tag: &'a str,
variant_name: &'a str,
variant_name: &'a Name,
},
Untagged,
}
Expand All @@ -880,7 +881,7 @@ fn serialize_struct_variant(
context: StructVariant,
params: &Parameters,
fields: &[Field],
name: &str,
name: &Name,
) -> Fragment {
if fields.iter().any(|field| field.attrs.flatten()) {
return serialize_struct_variant_with_flatten(context, params, fields, name);
Expand Down Expand Up @@ -964,7 +965,7 @@ fn serialize_struct_variant_with_flatten(
context: StructVariant,
params: &Parameters,
fields: &[Field],
name: &str,
name: &Name,
) -> Fragment {
let struct_trait = StructTrait::SerializeMap;
let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait);
Expand Down

0 comments on commit 373edcd

Please sign in to comment.