Skip to content

Commit 45cf4e5

Browse files
weiznichpksunkara
authored andcommitted
Fix compile fail message for mixed #[diesel(embed, serialize_as = _)]
1 parent 6aadc7f commit 45cf4e5

7 files changed

Lines changed: 131 additions & 37 deletions

File tree

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: `#[diesel(embed)]` cannot be combined with `#[diesel(serialize_as)]`
2-
--> $DIR/embed_and_serialize_as_cannot_be_mixed.rs:22:36
2+
--> $DIR/embed_and_serialize_as_cannot_be_mixed.rs:22:13
33
|
44
22 | #[diesel(embed, serialize_as = SomeType)]
5-
| ^^^^^^^^
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

diesel_derives/src/attrs.rs

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::fmt::{Display, Formatter};
22

33
use proc_macro2::{Span, TokenStream};
44
use proc_macro_error::ResultExt;
5+
use quote::spanned::Spanned;
56
use quote::ToTokens;
67
use syn::parse::discouraged::Speculative;
78
use syn::parse::{Parse, ParseStream, Parser, Result};
@@ -17,6 +18,12 @@ use util::{
1718
SQL_TYPE_NOTE, TABLE_NAME_NOTE, TREAT_NONE_AS_DEFAULT_VALUE_NOTE, TREAT_NONE_AS_NULL_NOTE,
1819
};
1920

21+
pub struct AttributeSpanWrapper<T> {
22+
pub item: T,
23+
pub attribute_span: Span,
24+
pub ident_span: Span,
25+
}
26+
2027
pub enum FieldAttr {
2128
Embed(Ident),
2229

@@ -118,6 +125,18 @@ impl Parse for FieldAttr {
118125
}
119126
}
120127

128+
impl Spanned for FieldAttr {
129+
fn __span(&self) -> Span {
130+
match self {
131+
FieldAttr::Embed(ident)
132+
| FieldAttr::ColumnName(ident, _)
133+
| FieldAttr::SqlType(ident, _)
134+
| FieldAttr::SerializeAs(ident, _)
135+
| FieldAttr::DeserializeAs(ident, _) => ident.span(),
136+
}
137+
}
138+
}
139+
121140
#[allow(clippy::large_enum_variant)]
122141
pub enum StructAttr {
123142
Aggregate(Ident),
@@ -203,15 +222,46 @@ impl Parse for StructAttr {
203222
}
204223
}
205224

206-
pub fn parse_attributes<T: Parse + ParseDeprecated>(attrs: &[Attribute]) -> Vec<T> {
225+
impl Spanned for StructAttr {
226+
fn __span(&self) -> Span {
227+
match self {
228+
StructAttr::Aggregate(ident)
229+
| StructAttr::NotSized(ident)
230+
| StructAttr::ForeignDerive(ident)
231+
| StructAttr::TableName(ident, _)
232+
| StructAttr::SqlType(ident, _)
233+
| StructAttr::TreatNoneAsDefaultValue(ident, _)
234+
| StructAttr::TreatNoneAsNull(ident, _)
235+
| StructAttr::BelongsTo(ident, _)
236+
| StructAttr::MysqlType(ident, _)
237+
| StructAttr::SqliteType(ident, _)
238+
| StructAttr::PostgresType(ident, _)
239+
| StructAttr::PrimaryKey(ident, _) => ident.span(),
240+
}
241+
}
242+
}
243+
244+
pub fn parse_attributes<T>(attrs: &[Attribute]) -> Vec<AttributeSpanWrapper<T>>
245+
where
246+
T: Parse + ParseDeprecated + Spanned,
247+
{
248+
use syn::spanned::Spanned;
249+
207250
attrs
208251
.iter()
209252
.flat_map(|attr| {
210253
if attr.path.is_ident("diesel") {
211254
attr.parse_args_with(Punctuated::<T, Comma>::parse_terminated)
212255
.unwrap_or_abort()
256+
.into_iter()
257+
.map(|a| AttributeSpanWrapper {
258+
ident_span: a.span(),
259+
item: a,
260+
attribute_span: attr.tokens.span(),
261+
})
262+
.collect::<Vec<_>>()
213263
} else {
214-
let mut p = Punctuated::new();
264+
let mut p = Vec::new();
215265
let Attribute { path, tokens, .. } = attr;
216266
let ident = path.get_ident().map(|f| f.to_string());
217267

@@ -223,10 +273,13 @@ pub fn parse_attributes<T: Parse + ParseDeprecated>(attrs: &[Attribute]) -> Vec<
223273
let value = Parser::parse(T::parse_deprecated, ts).unwrap_or_abort();
224274

225275
if let Some(value) = value {
226-
p.push_value(value);
276+
p.push(AttributeSpanWrapper {
277+
ident_span: value.span(),
278+
item: value,
279+
attribute_span: attr.tokens.span(),
280+
});
227281
}
228282
}
229-
230283
p
231284
}
232285
})

diesel_derives/src/field.rs

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ use quote::ToTokens;
33
use syn::spanned::Spanned;
44
use syn::{Field as SynField, Ident, Index, Type};
55

6-
use attrs::{parse_attributes, FieldAttr, SqlIdentifier};
6+
use attrs::{parse_attributes, AttributeSpanWrapper, FieldAttr, SqlIdentifier};
77

88
pub struct Field {
99
pub ty: Type,
1010
pub span: Span,
1111
pub name: FieldName,
12-
column_name: Option<SqlIdentifier>,
13-
pub sql_type: Option<Type>,
14-
pub serialize_as: Option<Type>,
15-
pub deserialize_as: Option<Type>,
16-
pub embed: bool,
12+
column_name: Option<AttributeSpanWrapper<SqlIdentifier>>,
13+
pub sql_type: Option<AttributeSpanWrapper<Type>>,
14+
pub serialize_as: Option<AttributeSpanWrapper<Type>>,
15+
pub deserialize_as: Option<AttributeSpanWrapper<Type>>,
16+
pub embed: Option<AttributeSpanWrapper<bool>>,
1717
}
1818

1919
impl Field {
@@ -26,15 +26,47 @@ impl Field {
2626
let mut sql_type = None;
2727
let mut serialize_as = None;
2828
let mut deserialize_as = None;
29-
let mut embed = false;
29+
let mut embed = None;
3030

3131
for attr in parse_attributes(attrs) {
32-
match attr {
33-
FieldAttr::ColumnName(_, value) => column_name = Some(value),
34-
FieldAttr::SqlType(_, value) => sql_type = Some(Type::Path(value)),
35-
FieldAttr::SerializeAs(_, value) => serialize_as = Some(Type::Path(value)),
36-
FieldAttr::DeserializeAs(_, value) => deserialize_as = Some(Type::Path(value)),
37-
FieldAttr::Embed(_) => embed = true,
32+
let attribute_span = attr.attribute_span;
33+
let ident_span = attr.ident_span;
34+
match attr.item {
35+
FieldAttr::ColumnName(_, value) => {
36+
column_name = Some(AttributeSpanWrapper {
37+
item: value,
38+
attribute_span,
39+
ident_span,
40+
})
41+
}
42+
FieldAttr::SqlType(_, value) => {
43+
sql_type = Some(AttributeSpanWrapper {
44+
item: Type::Path(value),
45+
attribute_span,
46+
ident_span,
47+
})
48+
}
49+
FieldAttr::SerializeAs(_, value) => {
50+
serialize_as = Some(AttributeSpanWrapper {
51+
item: Type::Path(value),
52+
attribute_span,
53+
ident_span,
54+
})
55+
}
56+
FieldAttr::DeserializeAs(_, value) => {
57+
deserialize_as = Some(AttributeSpanWrapper {
58+
item: Type::Path(value),
59+
attribute_span,
60+
ident_span,
61+
})
62+
}
63+
FieldAttr::Embed(_) => {
64+
embed = Some(AttributeSpanWrapper {
65+
item: true,
66+
attribute_span,
67+
ident_span,
68+
})
69+
}
3870
}
3971
}
4072

@@ -61,24 +93,31 @@ impl Field {
6193
}
6294

6395
pub fn column_name(&self) -> SqlIdentifier {
64-
self.column_name.clone().unwrap_or_else(|| match self.name {
65-
FieldName::Named(ref x) => x.into(),
66-
FieldName::Unnamed(ref x) => {
67-
abort!(
96+
self.column_name
97+
.as_ref()
98+
.map(|a| a.item.clone())
99+
.unwrap_or_else(|| match self.name {
100+
FieldName::Named(ref x) => x.into(),
101+
FieldName::Unnamed(ref x) => {
102+
abort!(
68103
x,
69104
"All fields of tuple structs must be annotated with `#[diesel(column_name)]`"
70105
);
71-
}
72-
})
106+
}
107+
})
73108
}
74109

75110
pub fn ty_for_deserialize(&self) -> &Type {
76-
if let Some(value) = &self.deserialize_as {
111+
if let Some(AttributeSpanWrapper { item: value, .. }) = &self.deserialize_as {
77112
value
78113
} else {
79114
&self.ty
80115
}
81116
}
117+
118+
pub(crate) fn embed(&self) -> bool {
119+
self.embed.as_ref().map(|a| a.item).unwrap_or(false)
120+
}
82121
}
83122

84123
pub enum FieldName {

diesel_derives/src/insertable.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use proc_macro2::TokenStream;
22
use syn::{DeriveInput, Expr, Path, Type};
33

4+
use attrs::AttributeSpanWrapper;
45
use field::Field;
56
use model::Model;
67
use util::{inner_of_option_ty, is_option_ty, wrap_in_dummy_mod};
@@ -25,7 +26,7 @@ pub fn derive(item: DeriveInput) -> TokenStream {
2526
let mut ref_field_assign = Vec::with_capacity(model.fields().len());
2627

2728
for field in model.fields() {
28-
match (field.serialize_as.as_ref(), field.embed) {
29+
match (field.serialize_as.as_ref(), field.embed()) {
2930
(None, true) => {
3031
direct_field_ty.push(field_ty_embed(field, None));
3132
direct_field_assign.push(field_expr_embed(field, None));
@@ -58,7 +59,7 @@ pub fn derive(item: DeriveInput) -> TokenStream {
5859
treat_none_as_default_value,
5960
));
6061
}
61-
(Some(ty), false) => {
62+
(Some(AttributeSpanWrapper { item: ty, .. }), false) => {
6263
direct_field_ty.push(field_ty_serialize_as(
6364
field,
6465
table_name,
@@ -74,9 +75,9 @@ pub fn derive(item: DeriveInput) -> TokenStream {
7475

7576
generate_borrowed_insert = false; // as soon as we hit one field with #[diesel(serialize_as)] there is no point in generating the impl of Insertable for borrowed structs
7677
}
77-
(Some(ty), true) => {
78+
(Some(AttributeSpanWrapper { attribute_span, .. }), true) => {
7879
abort!(
79-
ty,
80+
attribute_span,
8081
"`#[diesel(embed)]` cannot be combined with `#[diesel(serialize_as)]`"
8182
)
8283
}

diesel_derives/src/model.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl Model {
6363
let mut postgres_type = None;
6464

6565
for attr in parse_attributes(attrs) {
66-
match attr {
66+
match attr.item {
6767
StructAttr::SqlType(_, value) => sql_types.push(Type::Path(value)),
6868
StructAttr::TableName(_, value) => table_name = Some(value),
6969
StructAttr::PrimaryKey(_, keys) => {

diesel_derives/src/queryable_by_name.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use proc_macro2::{Span, TokenStream};
22
use syn::{DeriveInput, Ident, LitStr, Type};
33

4+
use attrs::AttributeSpanWrapper;
45
use field::{Field, FieldName};
56
use model::Model;
67
use util::wrap_in_dummy_mod;
@@ -15,7 +16,7 @@ pub fn derive(item: DeriveInput) -> TokenStream {
1516
let initial_field_expr = model.fields().iter().map(|f| {
1617
let field_ty = &f.ty;
1718

18-
if f.embed {
19+
if f.embed() {
1920
quote!(<#field_ty as QueryableByName<__DB>>::build(row)?)
2021
} else {
2122
let deserialize_ty = f.ty_for_deserialize();
@@ -39,7 +40,7 @@ pub fn derive(item: DeriveInput) -> TokenStream {
3940
for field in model.fields() {
4041
let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
4142
let field_ty = field.ty_for_deserialize();
42-
if field.embed {
43+
if field.embed() {
4344
where_clause
4445
.predicates
4546
.push(parse_quote!(#field_ty: QueryableByName<__DB>));
@@ -88,7 +89,7 @@ fn sql_type(field: &Field, model: &Model) -> Type {
8889
let table_name = model.table_name();
8990

9091
match field.sql_type {
91-
Some(ref st) => st.clone(),
92+
Some(AttributeSpanWrapper { item: ref st, .. }) => st.clone(),
9293
None => {
9394
if model.has_table_name_attribute() {
9495
let column_name = field.column_name();

diesel_derives/src/selectable.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub fn derive(item: DeriveInput) -> TokenStream {
1515
.params
1616
.push(parse_quote!(__DB: diesel::backend::Backend));
1717

18-
for embed_field in model.fields().iter().filter(|f| f.embed) {
18+
for embed_field in model.fields().iter().filter(|f| f.embed()) {
1919
let embed_ty = &embed_field.ty;
2020
generics
2121
.where_clause
@@ -48,7 +48,7 @@ pub fn derive(item: DeriveInput) -> TokenStream {
4848
}
4949

5050
fn field_column_ty(field: &Field, model: &Model) -> TokenStream {
51-
if field.embed {
51+
if field.embed() {
5252
let embed_ty = &field.ty;
5353
quote!(<#embed_ty as Selectable<__DB>>::SelectExpression)
5454
} else {
@@ -59,7 +59,7 @@ fn field_column_ty(field: &Field, model: &Model) -> TokenStream {
5959
}
6060

6161
fn field_column_inst(field: &Field, model: &Model) -> TokenStream {
62-
if field.embed {
62+
if field.embed() {
6363
let embed_ty = &field.ty;
6464
quote!(<#embed_ty as Selectable<__DB>>::construct_selection())
6565
} else {

0 commit comments

Comments
 (0)