Merge pull request #2855 from dtolnay/namespan

Produce unreachable_patterns warning when deserialization names collide
This commit is contained in:
David Tolnay
2024-11-10 23:21:46 -08:00
committed by GitHub
6 changed files with 174 additions and 94 deletions
+3 -2
View File
@@ -1,5 +1,6 @@
use crate::fragment::{Expr, Fragment, Match, Stmts}; use crate::fragment::{Expr, Fragment, Match, Stmts};
use crate::internals::ast::{Container, Data, Field, Style, Variant}; 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::internals::{attr, replace_receiver, ungroup, Ctxt, Derive};
use crate::{bound, dummy, pretend, this}; use crate::{bound, dummy, pretend, this};
use proc_macro2::{Literal, Span, TokenStream}; use proc_macro2::{Literal, Span, TokenStream};
@@ -2002,7 +2003,7 @@ fn deserialize_untagged_newtype_variant(
struct FieldWithAliases<'a> { struct FieldWithAliases<'a> {
ident: Ident, ident: Ident,
aliases: &'a BTreeSet<String>, aliases: &'a BTreeSet<Name>,
} }
fn deserialize_generated_identifier( fn deserialize_generated_identifier(
@@ -2224,7 +2225,7 @@ fn deserialize_identifier(
let aliases = field let aliases = field
.aliases .aliases
.iter() .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)) quote!(#(#aliases)|* => _serde::__private::Ok(#this_value::#ident))
}); });
+48 -84
View File
@@ -1,3 +1,4 @@
use crate::internals::name::{MultiName, Name};
use crate::internals::symbol::*; use crate::internals::symbol::*;
use crate::internals::{ungroup, Ctxt}; use crate::internals::{ungroup, Ctxt};
use proc_macro2::{Spacing, Span, TokenStream, TokenTree}; use proc_macro2::{Spacing, Span, TokenStream, TokenTree};
@@ -21,7 +22,7 @@ use syn::{parse_quote, token, Ident, Lifetime, Token};
pub use crate::internals::case::RenameRule; pub use crate::internals::case::RenameRule;
struct Attr<'c, T> { pub(crate) struct Attr<'c, T> {
cx: &'c Ctxt, cx: &'c Ctxt,
name: Symbol, name: Symbol,
tokens: TokenStream, tokens: TokenStream,
@@ -62,7 +63,7 @@ impl<'c, T> Attr<'c, T> {
} }
} }
fn get(self) -> Option<T> { pub(crate) fn get(self) -> Option<T> {
self.value self.value
} }
@@ -90,7 +91,7 @@ impl<'c> BoolAttr<'c> {
} }
} }
struct VecAttr<'c, T> { pub(crate) struct VecAttr<'c, T> {
cx: &'c Ctxt, cx: &'c Ctxt,
name: Symbol, name: Symbol,
first_dup_tokens: TokenStream, first_dup_tokens: TokenStream,
@@ -125,63 +126,13 @@ impl<'c, T> VecAttr<'c, T> {
} }
} }
fn get(self) -> Vec<T> { pub(crate) fn get(self) -> Vec<T> {
self.values self.values
} }
} }
pub struct Name { fn unraw(ident: &Ident) -> Ident {
serialize: String, Ident::new(ident.to_string().trim_start_matches("r#"), ident.span())
serialize_renamed: bool,
deserialize: String,
deserialize_renamed: bool,
deserialize_aliases: BTreeSet<String>,
}
fn unraw(ident: &Ident) -> String {
ident.to_string().trim_start_matches("r#").to_owned()
}
impl Name {
fn from_attrs(
source_name: String,
ser_name: Attr<String>,
de_name: Attr<String>,
de_aliases: Option<VecAttr<String>>,
) -> Name {
let mut alias_set = BTreeSet::new();
if let Some(de_aliases) = de_aliases {
for alias_name in de_aliases.get() {
alias_set.insert(alias_name);
}
}
let ser_name = ser_name.get();
let ser_renamed = ser_name.is_some();
let de_name = de_name.get();
let de_renamed = de_name.is_some();
Name {
serialize: ser_name.unwrap_or_else(|| source_name.clone()),
serialize_renamed: ser_renamed,
deserialize: de_name.unwrap_or(source_name),
deserialize_renamed: de_renamed,
deserialize_aliases: alias_set,
}
}
/// Return the container name for the container when serializing.
pub fn serialize_name(&self) -> &str {
&self.serialize
}
/// Return the container name for the container when deserializing.
pub fn deserialize_name(&self) -> &str {
&self.deserialize
}
fn deserialize_aliases(&self) -> &BTreeSet<String> {
&self.deserialize_aliases
}
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@@ -203,7 +154,7 @@ impl RenameAllRules {
/// Represents struct or enum attribute information. /// Represents struct or enum attribute information.
pub struct Container { pub struct Container {
name: Name, name: MultiName,
transparent: bool, transparent: bool,
deny_unknown_fields: bool, deny_unknown_fields: bool,
default: Default, default: Default,
@@ -327,8 +278,8 @@ impl Container {
// #[serde(rename = "foo")] // #[serde(rename = "foo")]
// #[serde(rename(serialize = "foo", deserialize = "bar"))] // #[serde(rename(serialize = "foo", deserialize = "bar"))]
let (ser, de) = get_renames(cx, RENAME, &meta)?; let (ser, de) = get_renames(cx, RENAME, &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));
de_name.set_opt(&meta.path, de.as_ref().map(syn::LitStr::value)); de_name.set_opt(&meta.path, de.as_ref().map(Name::from));
} else if meta.path == RENAME_ALL { } else if meta.path == RENAME_ALL {
// #[serde(rename_all = "foo")] // #[serde(rename_all = "foo")]
// #[serde(rename_all(serialize = "foo", deserialize = "bar"))] // #[serde(rename_all(serialize = "foo", deserialize = "bar"))]
@@ -567,7 +518,7 @@ impl Container {
} }
Container { Container {
name: Name::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(), transparent: transparent.get(),
deny_unknown_fields: deny_unknown_fields.get(), deny_unknown_fields: deny_unknown_fields.get(),
default: default.get().unwrap_or(Default::None), default: default.get().unwrap_or(Default::None),
@@ -594,7 +545,7 @@ impl Container {
} }
} }
pub fn name(&self) -> &Name { pub fn name(&self) -> &MultiName {
&self.name &self.name
} }
@@ -781,7 +732,7 @@ fn decide_identifier(
/// Represents variant attribute information /// Represents variant attribute information
pub struct Variant { pub struct Variant {
name: Name, name: MultiName,
rename_all_rules: RenameAllRules, rename_all_rules: RenameAllRules,
ser_bound: Option<Vec<syn::WherePredicate>>, ser_bound: Option<Vec<syn::WherePredicate>>,
de_bound: Option<Vec<syn::WherePredicate>>, de_bound: Option<Vec<syn::WherePredicate>>,
@@ -832,15 +783,15 @@ impl Variant {
// #[serde(rename = "foo")] // #[serde(rename = "foo")]
// #[serde(rename(serialize = "foo", deserialize = "bar"))] // #[serde(rename(serialize = "foo", deserialize = "bar"))]
let (ser, de) = get_multiple_renames(cx, &meta)?; 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 { for de_value in de {
de_name.set_if_none(de_value.value()); de_name.set_if_none(Name::from(&de_value));
de_aliases.insert(&meta.path, de_value.value()); de_aliases.insert(&meta.path, Name::from(&de_value));
} }
} else if meta.path == ALIAS { } else if meta.path == ALIAS {
// #[serde(alias = "foo")] // #[serde(alias = "foo")]
if let Some(s) = get_lit_str(cx, ALIAS, &meta)? { 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 { } else if meta.path == RENAME_ALL {
// #[serde(rename_all = "foo")] // #[serde(rename_all = "foo")]
@@ -947,7 +898,12 @@ impl Variant {
} }
Variant { Variant {
name: Name::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 { rename_all_rules: RenameAllRules {
serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None), serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None),
deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None), deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None),
@@ -964,20 +920,23 @@ impl Variant {
} }
} }
pub fn name(&self) -> &Name { pub fn name(&self) -> &MultiName {
&self.name &self.name
} }
pub fn aliases(&self) -> &BTreeSet<String> { pub fn aliases(&self) -> &BTreeSet<Name> {
self.name.deserialize_aliases() self.name.deserialize_aliases()
} }
pub fn rename_by_rules(&mut self, rules: RenameAllRules) { pub fn rename_by_rules(&mut self, rules: RenameAllRules) {
if !self.name.serialize_renamed { 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 { 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 self.name
.deserialize_aliases .deserialize_aliases
@@ -1023,7 +982,7 @@ impl Variant {
/// Represents field attribute information /// Represents field attribute information
pub struct Field { pub struct Field {
name: Name, name: MultiName,
skip_serializing: bool, skip_serializing: bool,
skip_deserializing: bool, skip_deserializing: bool,
skip_serializing_if: Option<syn::ExprPath>, skip_serializing_if: Option<syn::ExprPath>,
@@ -1082,8 +1041,11 @@ impl Field {
let mut flatten = BoolAttr::none(cx, FLATTEN); let mut flatten = BoolAttr::none(cx, FLATTEN);
let ident = match &field.ident { let ident = match &field.ident {
Some(ident) => unraw(ident), Some(ident) => Name::from(&unraw(ident)),
None => index.to_string(), None => Name {
value: index.to_string(),
span: Span::call_site(),
},
}; };
if let Some(borrow_attribute) = attrs.and_then(|variant| variant.borrow.as_ref()) { if let Some(borrow_attribute) = attrs.and_then(|variant| variant.borrow.as_ref()) {
@@ -1119,15 +1081,15 @@ impl Field {
// #[serde(rename = "foo")] // #[serde(rename = "foo")]
// #[serde(rename(serialize = "foo", deserialize = "bar"))] // #[serde(rename(serialize = "foo", deserialize = "bar"))]
let (ser, de) = get_multiple_renames(cx, &meta)?; 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 { for de_value in de {
de_name.set_if_none(de_value.value()); de_name.set_if_none(Name::from(&de_value));
de_aliases.insert(&meta.path, de_value.value()); de_aliases.insert(&meta.path, Name::from(&de_value));
} }
} else if meta.path == ALIAS { } else if meta.path == ALIAS {
// #[serde(alias = "foo")] // #[serde(alias = "foo")]
if let Some(s) = get_lit_str(cx, ALIAS, &meta)? { 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 { } else if meta.path == DEFAULT {
if meta.input.peek(Token![=]) { if meta.input.peek(Token![=]) {
@@ -1290,7 +1252,7 @@ impl Field {
} }
Field { Field {
name: Name::from_attrs(ident, ser_name, de_name, Some(de_aliases)), name: MultiName::from_attrs(ident, ser_name, de_name, Some(de_aliases)),
skip_serializing: skip_serializing.get(), skip_serializing: skip_serializing.get(),
skip_deserializing: skip_deserializing.get(), skip_deserializing: skip_deserializing.get(),
skip_serializing_if: skip_serializing_if.get(), skip_serializing_if: skip_serializing_if.get(),
@@ -1306,20 +1268,22 @@ impl Field {
} }
} }
pub fn name(&self) -> &Name { pub fn name(&self) -> &MultiName {
&self.name &self.name
} }
pub fn aliases(&self) -> &BTreeSet<String> { pub fn aliases(&self) -> &BTreeSet<Name> {
self.name.deserialize_aliases() self.name.deserialize_aliases()
} }
pub fn rename_by_rules(&mut self, rules: RenameAllRules) { pub fn rename_by_rules(&mut self, rules: RenameAllRules) {
if !self.name.serialize_renamed { 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 { 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 self.name
.deserialize_aliases .deserialize_aliases
@@ -1769,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. // attribute on the field so there must be at least one borrowable lifetime.
fn borrowable_lifetimes( fn borrowable_lifetimes(
cx: &Ctxt, cx: &Ctxt,
name: &str, name: &Name,
field: &syn::Field, field: &syn::Field,
) -> Result<BTreeSet<syn::Lifetime>, ()> { ) -> Result<BTreeSet<syn::Lifetime>, ()> {
let mut lifetimes = BTreeSet::new(); let mut lifetimes = BTreeSet::new();
+2 -2
View File
@@ -332,13 +332,13 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
let name = field.attrs.name(); let name = field.attrs.name();
let ser_name = name.serialize_name(); let ser_name = name.serialize_name();
if check_ser && ser_name == tag { if check_ser && ser_name.value == tag {
diagnose_conflict(); diagnose_conflict();
return; return;
} }
for de_name in field.attrs.aliases() { for de_name in field.attrs.aliases() {
if check_de && de_name == tag { if check_de && de_name.value == tag {
diagnose_conflict(); diagnose_conflict();
return; return;
} }
+1
View File
@@ -1,5 +1,6 @@
pub mod ast; pub mod ast;
pub mod attr; pub mod attr;
pub mod name;
mod case; mod case;
mod check; mod check;
+113
View File
@@ -0,0 +1,113 @@
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: Name,
pub(crate) serialize_renamed: bool,
pub(crate) deserialize: Name,
pub(crate) deserialize_renamed: bool,
pub(crate) deserialize_aliases: BTreeSet<Name>,
}
impl MultiName {
pub(crate) fn from_attrs(
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 {
for alias_name in de_aliases.get() {
alias_set.insert(alias_name);
}
}
let ser_name = ser_name.get();
let ser_renamed = ser_name.is_some();
let de_name = de_name.get();
let de_renamed = de_name.is_some();
MultiName {
serialize: ser_name.unwrap_or_else(|| source_name.clone()),
serialize_renamed: ser_renamed,
deserialize: de_name.unwrap_or(source_name),
deserialize_renamed: de_renamed,
deserialize_aliases: alias_set,
}
}
/// Return the container name for the container when serializing.
pub fn serialize_name(&self) -> &Name {
&self.serialize
}
/// Return the container name for the container when deserializing.
pub fn deserialize_name(&self) -> &Name {
&self.deserialize
}
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)
}
}
+7 -6
View File
@@ -1,5 +1,6 @@
use crate::fragment::{Fragment, Match, Stmts}; use crate::fragment::{Fragment, Match, Stmts};
use crate::internals::ast::{Container, Data, Field, Style, Variant}; use crate::internals::ast::{Container, Data, Field, Style, Variant};
use crate::internals::name::Name;
use crate::internals::{attr, replace_receiver, Ctxt, Derive}; use crate::internals::{attr, replace_receiver, Ctxt, Derive};
use crate::{bound, dummy, pretend, this}; use crate::{bound, dummy, pretend, this};
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
@@ -798,9 +799,9 @@ fn serialize_untagged_variant(
enum TupleVariant<'a> { enum TupleVariant<'a> {
ExternallyTagged { ExternallyTagged {
type_name: &'a str, type_name: &'a Name,
variant_index: u32, variant_index: u32,
variant_name: &'a str, variant_name: &'a Name,
}, },
Untagged, Untagged,
} }
@@ -867,11 +868,11 @@ fn serialize_tuple_variant(
enum StructVariant<'a> { enum StructVariant<'a> {
ExternallyTagged { ExternallyTagged {
variant_index: u32, variant_index: u32,
variant_name: &'a str, variant_name: &'a Name,
}, },
InternallyTagged { InternallyTagged {
tag: &'a str, tag: &'a str,
variant_name: &'a str, variant_name: &'a Name,
}, },
Untagged, Untagged,
} }
@@ -880,7 +881,7 @@ fn serialize_struct_variant(
context: StructVariant, context: StructVariant,
params: &Parameters, params: &Parameters,
fields: &[Field], fields: &[Field],
name: &str, name: &Name,
) -> Fragment { ) -> Fragment {
if fields.iter().any(|field| field.attrs.flatten()) { if fields.iter().any(|field| field.attrs.flatten()) {
return serialize_struct_variant_with_flatten(context, params, fields, name); return serialize_struct_variant_with_flatten(context, params, fields, name);
@@ -964,7 +965,7 @@ fn serialize_struct_variant_with_flatten(
context: StructVariant, context: StructVariant,
params: &Parameters, params: &Parameters,
fields: &[Field], fields: &[Field],
name: &str, name: &Name,
) -> Fragment { ) -> Fragment {
let struct_trait = StructTrait::SerializeMap; let struct_trait = StructTrait::SerializeMap;
let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait); let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait);