Skip to content

Commit

Permalink
Merge pull request #3794 from weiznich/fix/3790
Browse files Browse the repository at this point in the history
Fix #3790
  • Loading branch information
weiznich committed Sep 22, 2023
1 parent 8246b23 commit 4f985d7
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 50 deletions.
7 changes: 7 additions & 0 deletions diesel_compile_tests/tests/fail/derive/selectable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,11 @@ struct User {
no_tuple: i32,
}

#[derive(Selectable)]
#[diesel(table_name = users)]
#[diesel(check_for_backend(diesel::pg::Pg))]
struct User1<'a> {
name: &'a str,
}

fn main() {}
7 changes: 7 additions & 0 deletions diesel_compile_tests/tests/fail/derive/selectable.stderr
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
error: References are not supported in `Queryable` types
Consider using `std::borrow::Cow<'a, str>` instead
--> tests/fail/derive/selectable.rs:48:11
|
48 | name: &'a str,
| ^^^^^^^

error[E0412]: cannot find type `non_existing` in module `users`
--> tests/fail/derive/selectable.rs:26:5
|
Expand Down
81 changes: 31 additions & 50 deletions diesel_derives/src/selectable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::util::wrap_in_dummy_mod;
pub fn derive(item: DeriveInput) -> Result<TokenStream> {
let model = Model::from_item(&item, false, false)?;

let (_, ty_generics, _) = item.generics.split_for_impl();
let (_, ty_generics, original_where_clause) = item.generics.split_for_impl();

let mut generics = item.generics.clone();
generics
Expand Down Expand Up @@ -50,20 +50,23 @@ pub fn derive(item: DeriveInput) -> Result<TokenStream> {
.filter(|(f, _)| !f.embed())
.flat_map(|(f, ty)| {
backends.iter().map(move |b| {
let field_ty = to_field_ty_bound(f.ty_for_deserialize());
let span = field_ty.span();
quote::quote_spanned! {span =>
let span = f.ty.span();
let field_ty = to_field_ty_bound(f.ty_for_deserialize())?;
Ok(syn::parse_quote_spanned! {span =>
#field_ty: diesel::deserialize::FromSqlRow<diesel::dsl::SqlTypeOf<#ty>, #b>
}
})
})
});
})
.collect::<Result<Vec<_>>>()?;
let where_clause = &mut original_where_clause.cloned();
let where_clause = where_clause.get_or_insert_with(|| parse_quote!(where));
for field_check in field_check_bound {
where_clause.predicates.push(field_check);
}
Some(quote::quote! {
fn _check_field_compatibility()
where
#(#field_check_bound,)*
{

}
fn _check_field_compatibility #impl_generics()
#where_clause
{}
})
} else {
None
Expand All @@ -87,49 +90,27 @@ pub fn derive(item: DeriveInput) -> Result<TokenStream> {
}))
}

fn to_field_ty_bound(field_ty: &syn::Type) -> Option<TokenStream> {
fn to_field_ty_bound(field_ty: &syn::Type) -> Result<TokenStream> {
match field_ty {
syn::Type::Path(p) => {
if let syn::PathArguments::AngleBracketed(ref args) =
p.path.segments.last().unwrap().arguments
{
let lt = args
.args
.iter()
.filter_map(|f| {
if let syn::GenericArgument::Lifetime(lt) = f {
Some(lt)
} else {
None
}
})
.collect::<Vec<_>>();
if lt.is_empty() {
Some(quote::quote! {
#field_ty
})
} else if lt.len() == args.args.len() {
Some(quote::quote! {
for<#(#lt,)*> #field_ty
})
} else {
// type parameters are not supported for checking
// for now
None
}
} else {
Some(quote::quote! {
#field_ty
})
}
}
syn::Type::Reference(_r) => {
syn::Type::Reference(r) => {
use crate::quote::ToTokens;
// references are not supported for checking for now
//
// (How ever you can even have references in a `Queryable` struct anyway)
None
Err(syn::Error::new(
field_ty.span(),
format!(
"References are not supported in `Queryable` types\n\
Consider using `std::borrow::Cow<'{}, {}>` instead",
r.lifetime
.as_ref()
.expect("It's a struct field so it must have a named lifetime")
.ident,
r.elem.to_token_stream()
),
))
}
field_ty => Some(quote::quote! {
field_ty => Ok(quote::quote! {
#field_ty
}),
}
Expand Down
51 changes: 51 additions & 0 deletions diesel_derives/tests/selectable.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use std::marker::PhantomData;

use diesel::deserialize::FromSql;
use diesel::sql_types::Text;
use diesel::*;

use crate::helpers::connection;
Expand Down Expand Up @@ -172,3 +176,50 @@ fn check_for_backend_with_deserialize_as() {
name: MyString,
}
}

#[allow(dead_code)] // that's essentially a compile test
#[test]
fn check_with_lifetime_and_type_param() {
use std::borrow::Cow;
table! {
test {
id -> Integer,
name -> Text,
}
}

#[derive(Queryable, Selectable)]
#[diesel(table_name = test)]
#[diesel(check_for_backend(crate::helpers::TestBackend))]
pub struct Account<'n0> {
id: i32,
name: Cow<'n0, str>,
}

#[derive(Queryable, Selectable)]
#[diesel(table_name = test)]
#[diesel(check_for_backend(crate::helpers::TestBackend))]
pub struct Foo<T>
where
T: Copy,
{
name: FooInner<T>,
}

#[derive(FromSqlRow)]
pub struct FooInner<T>(String, PhantomData<T>);

impl<T> FromSql<Text, crate::helpers::TestBackend> for FooInner<T>
where
T: Copy,
{
fn from_sql(
bytes: <crate::helpers::TestBackend as backend::Backend>::RawValue<'_>,
) -> deserialize::Result<Self> {
Ok(Self(
<String as FromSql<Text, crate::helpers::TestBackend>>::from_sql(bytes)?,
PhantomData,
))
}
}
}

0 comments on commit 4f985d7

Please sign in to comment.