Repository: diesel-rs/diesel (language: rust)

Issue:
error: expected one of `{`, lifetime, or type, found `:`
<!--
If you want to report a bug, we added some points below which help us track down the problem faster.
-->

## Setup

### Versions

- **Rust:** `rustc 1.70.0 (90c541806 2023-05-31)`
- **Diesel:** `diesel 2.1.1`
- **Database:** mysql 
- **Operating System** MacOS Ventura 13.4.1 (M2 Chipset)

### Feature Flags

- **diesel:** mysql, uuid

## Problem Description


### What are you trying to accomplish?
I am trying to create a model to correspond with the schema generated from my migrations, following the guide [here](https://diesel.rs/guides/getting-started) but adapting the instructions for Pg to Mysql.


### What is the expected output?
The model should compile. I started with copying and pasting the attributes defined in the Getting Started guide, and then filled in the proper details for my struct according to the generated schema defined in `schema.rs`.


### What is the actual output?

```
error: expected one of `{`, lifetime, or type, found `:`
 --> server/src/database/models.rs:4:21
  |
4 | #[derive(Queryable, Selectable)]
  |                     ^^^^^^^^^^ expected one of `{`, lifetime, or type
  |
  = note: this error originates in the derive macro `Selectable` (in Nightly builds, run with -Z macro-backtrace for more info)
```

### Are you seeing any additional errors?
There is a separate but probably related error here:
```
error: proc-macro derive produced unparsable tokens
 --> server/src/database/models.rs:4:21
  |
4 | #[derive(Queryable, Selectable)]
  | 
```

### Steps to reproduce

Relevant source (lines 1-7):
```
use diesel::prelude::*;
use uuid::Uuid;

#[derive(Queryable, Selectable)]
#[diesel(table_name = crate::database::schema::account)]
#[diesel(check_for_backend(diesel::mysql::Mysql))]
pub struct Account<'n0, 'n1> {
// ... struct properties are defined here, aligning with schema.rs
}
```

While I cannot share the migration file, or the schema itself, I can point out that it uses the UUID type in MySQL and aligns that with the `Uuid` struct defined by the `uuid` package.

Segment of cargo.toml
```
[dependencies]
prost = "0.11.9"
tonic = "0.9.2"
tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"] }
tonic-reflection = "0.9.2"
diesel = { version = "2.1.0", features = ["mysql", "uuid"] }
dotenvy = "0.15"
uuid = "1.4.1"

[build-dependencies]
tonic-build = "0.9.2"
```

## Checklist

- [X] I have already looked over the [issue tracker](https://github.com/diesel-rs/diesel/issues) and the [discussion forum](https://github.com/diesel-rs/diesel/discussions) for similar possible closed issues.
<!--
If you are unsure if your issue is a duplicate of an existing issue please link the issue in question here
--> 
- [X] This issue can be reproduced on Rust's stable channel. (Your issue will be
  closed if this is not the case)
- [X] This issue can be reproduced without requiring a third party crate

<!--
Thank you for your submission!  You're helping make Diesel more robust 🎉

We'll try to respond as quickly as possible.
-->



Discussion / hints:
This error appears to be resolved when removing the lifetimes `n0` and `n1`.
Thanks for reporting this bug. 

Can you provide the complete definition of the `Account` type, including all fields? That would make it much easier to reproduce the problem.

Fix patch:
diff --git a/diesel_derives/src/selectable.rs b/diesel_derives/src/selectable.rs
index 352a86d65841..5d2684bd8027 100644
--- a/diesel_derives/src/selectable.rs
+++ b/diesel_derives/src/selectable.rs
@@ -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
@@ -51,20 +51,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
@@ -95,49 +98,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
         }),
     }


Test patch:
diff --git a/diesel_compile_tests/tests/fail/derive/selectable.rs b/diesel_compile_tests/tests/fail/derive/selectable.rs
index 5edee02f5912..79c913e9efea 100644
--- a/diesel_compile_tests/tests/fail/derive/selectable.rs
+++ b/diesel_compile_tests/tests/fail/derive/selectable.rs
@@ -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() {}
diff --git a/diesel_compile_tests/tests/fail/derive/selectable.stderr b/diesel_compile_tests/tests/fail/derive/selectable.stderr
index 37dd257c4b05..bfd06d17b43d 100644
--- a/diesel_compile_tests/tests/fail/derive/selectable.stderr
+++ b/diesel_compile_tests/tests/fail/derive/selectable.stderr
@@ -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
    |
diff --git a/diesel_derives/tests/selectable.rs b/diesel_derives/tests/selectable.rs
index af1c73e4591c..4b151f7fc2af 100644
--- a/diesel_derives/tests/selectable.rs
+++ b/diesel_derives/tests/selectable.rs
@@ -1,3 +1,7 @@
+use std::marker::PhantomData;
+
+use diesel::deserialize::FromSql;
+use diesel::sql_types::Text;
 use diesel::*;
 
 use crate::helpers::connection;
@@ -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,
+            ))
+        }
+    }
+}