-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4054 from weiznich/fix/auto_type_lifetimes
Fix lifetimes with `#[auto_type]`
- Loading branch information
Showing
8 changed files
with
228 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
use diesel::dsl::*; | ||
use diesel::prelude::*; | ||
|
||
diesel::table! { | ||
users { | ||
id -> Integer, | ||
name -> Text, | ||
} | ||
} | ||
|
||
#[auto_type] | ||
fn with_lifetime(name: &'_ str) -> _ { | ||
users::table.filter(users::name.eq(name)) | ||
} | ||
|
||
#[auto_type] | ||
fn with_lifetime2(name: &str) -> _ { | ||
users::table.filter(users::name.eq(name)) | ||
} | ||
|
||
fn main() { | ||
println!("Hello, world!"); | ||
} |
49 changes: 49 additions & 0 deletions
49
diesel_compile_tests/tests/fail/auto_type_life_times.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
error: `#[auto_type]` requires named lifetimes | ||
--> tests/fail/auto_type_life_times.rs:12:25 | ||
| | ||
12 | fn with_lifetime(name: &'_ str) -> _ { | ||
| ^^ | ||
|
||
error: `#[auto_type]` requires named lifetimes | ||
--> tests/fail/auto_type_life_times.rs:17:25 | ||
| | ||
17 | fn with_lifetime2(name: &str) -> _ { | ||
| ^^^^ | ||
|
||
error[E0106]: missing lifetime specifier | ||
--> tests/fail/auto_type_life_times.rs:12:25 | ||
| | ||
12 | fn with_lifetime(name: &'_ str) -> _ { | ||
| ^^ expected named lifetime parameter | ||
| | ||
help: consider introducing a named lifetime parameter | ||
| | ||
12 | fn with_lifetime<'a>(name: &'a str) -> _ { | ||
| ++++ ~~ | ||
|
||
error[E0106]: missing lifetime specifier | ||
--> tests/fail/auto_type_life_times.rs:17:25 | ||
| | ||
17 | fn with_lifetime2(name: &str) -> _ { | ||
| ^ expected named lifetime parameter | ||
| | ||
help: consider introducing a named lifetime parameter | ||
| | ||
17 | fn with_lifetime2<'a>(name: &'a str) -> _ { | ||
| ++++ ++ | ||
|
||
error: lifetime may not live long enough | ||
--> tests/fail/auto_type_life_times.rs:13:5 | ||
| | ||
12 | fn with_lifetime(name: &'_ str) -> _ { | ||
| - let's call the lifetime of this reference `'1` | ||
13 | users::table.filter(users::name.eq(name)) | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` | ||
|
||
error: lifetime may not live long enough | ||
--> tests/fail/auto_type_life_times.rs:18:5 | ||
| | ||
17 | fn with_lifetime2(name: &str) -> _ { | ||
| - let's call the lifetime of this reference `'1` | ||
18 | users::table.filter(users::name.eq(name)) | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
use std::rc::Rc; | ||
use syn::parse_quote; | ||
use syn::visit::{self, Visit}; | ||
use syn::{Ident, Lifetime}; | ||
|
||
pub(crate) fn extract_referenced_generics( | ||
ty: &syn::Type, | ||
generics: &syn::Generics, | ||
errors: &mut Vec<Rc<syn::Error>>, | ||
) -> syn::Generics { | ||
struct Visitor<'g, 'errs> { | ||
lifetimes: Vec<(&'g Lifetime, bool)>, | ||
type_parameters: Vec<(&'g Ident, bool)>, | ||
errors: &'errs mut Vec<Rc<syn::Error>>, | ||
} | ||
|
||
let mut visitor = Visitor { | ||
lifetimes: generics | ||
.lifetimes() | ||
.map(|lt| (<.lifetime, false)) | ||
.collect(), | ||
type_parameters: generics | ||
.type_params() | ||
.map(|tp| (&tp.ident, false)) | ||
.collect(), | ||
errors, | ||
}; | ||
visitor.lifetimes.sort_unstable(); | ||
visitor.type_parameters.sort_unstable(); | ||
|
||
impl<'ast> Visit<'ast> for Visitor<'_, '_> { | ||
fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) { | ||
if lifetime.ident == "_" { | ||
self.errors.push(Rc::new(syn::Error::new_spanned( | ||
lifetime, | ||
"`#[auto_type]` requires named lifetimes", | ||
))); | ||
} else if lifetime.ident != "static" { | ||
if let Ok(lifetime_idx) = self | ||
.lifetimes | ||
.binary_search_by_key(&lifetime, |(lt, _)| *lt) | ||
{ | ||
self.lifetimes[lifetime_idx].1 = true; | ||
} | ||
} | ||
visit::visit_lifetime(self, lifetime) | ||
} | ||
|
||
fn visit_type_reference(&mut self, reference: &'ast syn::TypeReference) { | ||
if reference.lifetime.is_none() { | ||
self.errors.push(Rc::new(syn::Error::new_spanned( | ||
reference, | ||
"`#[auto_type]` requires named lifetimes", | ||
))); | ||
} | ||
visit::visit_type_reference(self, reference) | ||
} | ||
|
||
fn visit_type_path(&mut self, type_path: &'ast syn::TypePath) { | ||
if let Some(path_ident) = type_path.path.get_ident() { | ||
if let Ok(type_param_idx) = self | ||
.type_parameters | ||
.binary_search_by_key(&path_ident, |tp| tp.0) | ||
{ | ||
self.type_parameters[type_param_idx].1 = true; | ||
} | ||
} | ||
visit::visit_type_path(self, type_path) | ||
} | ||
} | ||
|
||
visitor.visit_type(ty); | ||
|
||
let generic_params: syn::punctuated::Punctuated<syn::GenericParam, _> = generics | ||
.params | ||
.iter() | ||
.filter_map(|param| match param { | ||
syn::GenericParam::Lifetime(lt) | ||
if visitor | ||
.lifetimes | ||
.binary_search(&(<.lifetime, true)) | ||
.is_ok() => | ||
{ | ||
let lt = <.lifetime; | ||
Some(parse_quote!(#lt)) | ||
} | ||
syn::GenericParam::Type(tp) | ||
if visitor | ||
.type_parameters | ||
.binary_search(&(&tp.ident, true)) | ||
.is_ok() => | ||
{ | ||
let ident = &tp.ident; | ||
Some(parse_quote!(#ident)) | ||
} | ||
_ => None::<syn::GenericParam>, | ||
}) | ||
.collect(); | ||
|
||
// We need to not set the lt_token and gt_token if `params` is empty to get | ||
// a reasonable error message for the case that there is no lifetime specifier | ||
// but we need one | ||
syn::Generics { | ||
lt_token: (!generic_params.is_empty()).then(Default::default), | ||
gt_token: (!generic_params.is_empty()).then(Default::default), | ||
params: generic_params, | ||
where_clause: None, | ||
} | ||
} |