Skip to content

Commit 8f48569

Browse files
committed
Rewrite #[derive(QueryableByName)] in derives2
Since `QueryableByName` is one of the more recently written derives, it should have been a really straightforward port. Unfortunately, the tests for this derive hit multiple rustc bugs - rust-lang/rust#47983 - rust-lang/rust#47311 I love what we were able to do with the error message here. We could even go so far as to have the `help` lines point at the struct itself for the `table_name` annotation if we want to. I also much prefer the workaround for rust-lang/rust#47311 in this PR to the one I did in #1529. I'll need to update that PR if this is merged first.
1 parent 14887fc commit 8f48569

17 files changed

+245
-128
lines changed

diesel_compile_tests/tests/compile-fail/queryable_by_name_requires_table_name_or_sql_type_annotation.rs

-9
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#[macro_use] extern crate diesel;
2+
3+
#[derive(QueryableByName)]
4+
struct Foo {
5+
foo: i32,
6+
bar: String,
7+
}
8+
9+
#[derive(QueryableByName)]
10+
struct Bar(i32, String);
11+
12+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
error: Cannot determine the SQL type of foo
2+
--> $DIR/queryable_by_name_requires_table_name_or_sql_type_annotation.rs:5:5
3+
|
4+
5 | foo: i32,
5+
| ^^^
6+
|
7+
= help: Your struct must either be annotated with `#[table_name = "foo"]` or have all of its fields annotated with `#[sql_type = "Integer"]`
8+
9+
error: Cannot determine the SQL type of bar
10+
--> $DIR/queryable_by_name_requires_table_name_or_sql_type_annotation.rs:6:5
11+
|
12+
6 | bar: String,
13+
| ^^^
14+
|
15+
= help: Your struct must either be annotated with `#[table_name = "foo"]` or have all of its fields annotated with `#[sql_type = "Integer"]`
16+
17+
error: All fields of tuple structs must be annotated with `#[column_name]`
18+
--> $DIR/queryable_by_name_requires_table_name_or_sql_type_annotation.rs:10:12
19+
|
20+
10 | struct Bar(i32, String);
21+
| ^^^
22+
23+
error: Cannot determine the SQL type of field
24+
--> $DIR/queryable_by_name_requires_table_name_or_sql_type_annotation.rs:10:12
25+
|
26+
10 | struct Bar(i32, String);
27+
| ^^^
28+
|
29+
= help: Your struct must either be annotated with `#[table_name = "foo"]` or have all of its fields annotated with `#[sql_type = "Integer"]`
30+
31+
error: All fields of tuple structs must be annotated with `#[column_name]`
32+
--> $DIR/queryable_by_name_requires_table_name_or_sql_type_annotation.rs:10:17
33+
|
34+
10 | struct Bar(i32, String);
35+
| ^^^^^^
36+
37+
error: Cannot determine the SQL type of field
38+
--> $DIR/queryable_by_name_requires_table_name_or_sql_type_annotation.rs:10:17
39+
|
40+
10 | struct Bar(i32, String);
41+
| ^^^^^^
42+
|
43+
= help: Your struct must either be annotated with `#[table_name = "foo"]` or have all of its fields annotated with `#[sql_type = "Integer"]`
44+
45+
error: aborting due to 6 previous errors
46+

diesel_derives/src/attr.rs

-12
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,6 @@ impl Attr {
4949
)
5050
}
5151

52-
pub fn sql_type(&self) -> Option<&syn::Ty> {
53-
self.sql_type.as_ref()
54-
}
55-
56-
pub fn has_flag<T>(&self, flag: &T) -> bool
57-
where
58-
T: ?Sized,
59-
syn::Ident: PartialEq<T>,
60-
{
61-
self.flags.iter().any(|f| f == flag)
62-
}
63-
6452
fn field_kind(&self) -> &str {
6553
if is_option_ty(&self.ty) {
6654
"option"

diesel_derives/src/lib.rs

-6
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ mod insertable;
3333
mod model;
3434
mod query_id;
3535
mod queryable;
36-
mod queryable_by_name;
3736
mod sql_type;
3837
mod util;
3938

@@ -45,11 +44,6 @@ pub fn derive_queryable(input: TokenStream) -> TokenStream {
4544
expand_derive(input, queryable::derive_queryable)
4645
}
4746

48-
#[proc_macro_derive(QueryableByName, attributes(table_name, column_name, sql_type, diesel))]
49-
pub fn derive_queryable_by_name(input: TokenStream) -> TokenStream {
50-
expand_derive(input, queryable_by_name::derive)
51-
}
52-
5347
#[proc_macro_derive(Insertable, attributes(table_name, column_name))]
5448
pub fn derive_insertable(input: TokenStream) -> TokenStream {
5549
expand_derive(input, insertable::derive_insertable)

diesel_derives/src/queryable_by_name.rs

-76
This file was deleted.

diesel_derives/tests/tests.rs

-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,5 @@ extern crate diesel;
66
extern crate diesel_derives;
77

88
mod queryable;
9-
mod queryable_by_name;
109
mod associations;
1110
mod test_helpers;

diesel_derives2/src/as_changeset.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ fn field_changeset_expr(
8686
table_name: syn::Ident,
8787
treat_none_as_null: bool,
8888
) -> syn::Expr {
89-
let field_access = &field.name;
89+
let field_access = field.name.access();
9090
let column_name = field.column_name();
9191
if !treat_none_as_null && is_option_ty(&field.ty) {
9292
parse_quote!(self#field_access.as_ref().map(|x| #table_name::#column_name.eq(x)))

diesel_derives2/src/field.rs

+44-12
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,49 @@
11
use proc_macro2::Span;
22
use quote;
3-
use syn;
43
use syn::spanned::Spanned;
4+
use syn;
55

6-
use diagnostic_shim::*;
76
use meta::*;
7+
use util::*;
88

99
pub struct Field {
1010
pub ty: syn::Type,
1111
pub name: FieldName,
12+
pub span: Span,
13+
pub sql_type: Option<syn::Type>,
1214
column_name_from_attribute: Option<syn::Ident>,
15+
flags: MetaItem,
1316
}
1417

1518
impl Field {
1619
pub fn from_struct_field(field: &syn::Field, index: usize) -> Self {
1720
let column_name_from_attribute =
1821
MetaItem::with_name(&field.attrs, "column_name").map(|m| m.expect_ident_value());
1922
let name = match field.ident {
20-
Some(x) => FieldName::Named(x),
23+
Some(mut x) => {
24+
// https://github.com/rust-lang/rust/issues/47983#issuecomment-362817105
25+
x.span = fix_span(x.span, Span::call_site());
26+
FieldName::Named(x)
27+
}
2128
None => FieldName::Unnamed(syn::Index {
2229
index: index as u32,
2330
// https://github.com/rust-lang/rust/issues/47312
2431
span: Span::call_site(),
2532
}),
2633
};
34+
let sql_type = MetaItem::with_name(&field.attrs, "sql_type")
35+
.and_then(|m| m.ty_value().map_err(Diagnostic::emit).ok());
36+
let flags = MetaItem::with_name(&field.attrs, "diesel")
37+
.unwrap_or_else(|| MetaItem::empty("diesel"));
38+
let span = field.span();
2739

2840
Self {
2941
ty: field.ty.clone(),
3042
column_name_from_attribute,
3143
name,
44+
sql_type,
45+
flags,
46+
span,
3247
}
3348
}
3449

@@ -37,8 +52,7 @@ impl Field {
3752
.unwrap_or_else(|| match self.name {
3853
FieldName::Named(x) => x,
3954
_ => {
40-
self.ty
41-
.span()
55+
self.span
4256
.error(
4357
"All fields of tuple structs must be annotated with `#[column_name]`",
4458
)
@@ -47,22 +61,40 @@ impl Field {
4761
}
4862
})
4963
}
64+
65+
pub fn has_flag(&self, flag: &str) -> bool {
66+
self.flags.has_flag(flag)
67+
}
5068
}
5169

5270
pub enum FieldName {
5371
Named(syn::Ident),
5472
Unnamed(syn::Index),
5573
}
5674

75+
impl FieldName {
76+
pub fn assign(&self, expr: syn::Expr) -> syn::FieldValue {
77+
let span = self.span();
78+
// Parens are to work around https://github.com/rust-lang/rust/issues/47311
79+
let tokens = quote_spanned!(span=> #self: (#expr));
80+
parse_quote!(#tokens)
81+
}
82+
83+
pub fn access(&self) -> quote::Tokens {
84+
let span = self.span();
85+
quote_spanned!(span=> .#self)
86+
}
87+
88+
pub fn span(&self) -> Span {
89+
match *self {
90+
FieldName::Named(x) => x.span,
91+
FieldName::Unnamed(ref x) => x.span,
92+
}
93+
}
94+
}
95+
5796
impl quote::ToTokens for FieldName {
5897
fn to_tokens(&self, tokens: &mut quote::Tokens) {
59-
use proc_macro2::{Spacing, TokenNode, TokenTree};
60-
61-
// https://github.com/rust-lang/rust/issues/47312
62-
tokens.append(TokenTree {
63-
span: Span::call_site(),
64-
kind: TokenNode::Op('.', Spacing::Alone),
65-
});
6698
match *self {
6799
FieldName::Named(x) => x.to_tokens(tokens),
68100
FieldName::Unnamed(ref x) => x.to_tokens(tokens),

diesel_derives2/src/identifiable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub fn derive(item: syn::DeriveInput) -> Result<quote::Tokens, Diagnostic> {
1818
.primary_key_names
1919
.iter()
2020
.filter_map(|&pk| model.find_column(pk).map_err(Diagnostic::emit).ok())
21-
.map(|f| (&f.ty, &f.name))
21+
.map(|f| (&f.ty, f.name.access()))
2222
.unzip();
2323

2424
Ok(wrap_in_dummy_mod(

diesel_derives2/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ mod util;
2828

2929
mod as_changeset;
3030
mod identifiable;
31+
mod queryable_by_name;
3132

3233
use diagnostic_shim::*;
3334

@@ -42,6 +43,11 @@ pub fn derive_identifiable(input: TokenStream) -> TokenStream {
4243
expand_derive(input, identifiable::derive)
4344
}
4445

46+
#[proc_macro_derive(QueryableByName, attributes(table_name, column_name, sql_type, diesel))]
47+
pub fn derive_queryable_by_name(input: TokenStream) -> TokenStream {
48+
expand_derive(input, queryable_by_name::derive)
49+
}
50+
4551
fn expand_derive(
4652
input: TokenStream,
4753
f: fn(syn::DeriveInput) -> Result<quote::Tokens, Diagnostic>,

0 commit comments

Comments
 (0)