-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add implementation for
#[blanket(derive(Rc))]
with associated tests
- Loading branch information
Showing
13 changed files
with
373 additions
and
9 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
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,149 @@ | ||
use syn::parse_quote; | ||
use syn::spanned::Spanned; | ||
|
||
use crate::utils::deref_expr; | ||
use crate::utils::signature_to_method_call; | ||
use crate::utils::trait_to_generic_ident; | ||
|
||
pub fn derive(trait_: &syn::ItemTrait) -> syn::Result<syn::ItemImpl> { | ||
// build the methods | ||
let mut methods: Vec<syn::ImplItemMethod> = Vec::new(); | ||
for item in trait_.items.iter() { | ||
if let syn::TraitItem::Method(ref m) = item { | ||
if let Some(receiver) = m.sig.receiver() { | ||
match receiver { | ||
syn::FnArg::Receiver(r) if r.mutability.is_some() => { | ||
let msg = "cannot derive `Rc` for a trait declaring `&mut self` methods"; | ||
return Err(syn::Error::new(r.span(), msg)); | ||
} | ||
syn::FnArg::Receiver(r) if r.reference.is_none() => { | ||
let msg = "cannot derive `Rc` for a trait declaring `self` methods"; | ||
return Err(syn::Error::new(r.span(), msg)); | ||
} | ||
syn::FnArg::Typed(pat) => { | ||
let msg = "cannot derive `Rc` for a trait declaring methods with arbitrary receiver types"; | ||
return Err(syn::Error::new(pat.span(), msg)); | ||
} | ||
_ => (), | ||
} | ||
} | ||
|
||
let mut call = signature_to_method_call(&m.sig)?; | ||
call.receiver = Box::new(deref_expr(deref_expr(*call.receiver))); | ||
|
||
let signature = &m.sig; | ||
let item = parse_quote!(#[inline] #signature { #call }); | ||
methods.push(item) | ||
} | ||
} | ||
|
||
// build an identifier for the generic type used for the implementation | ||
let trait_ident = &trait_.ident; | ||
let generic_type = trait_to_generic_ident(&trait_); | ||
|
||
// build the generics for the impl block: | ||
// we use the same generics as the trait itself, plus | ||
// a generic type that implements the trait for which we provide the | ||
// blanket implementation | ||
let trait_generics = &trait_.generics; | ||
let mut impl_generics = trait_generics.clone(); | ||
impl_generics.params.push(syn::GenericParam::Type( | ||
parse_quote!(#generic_type: #trait_ident #trait_generics + ?Sized), | ||
)); | ||
|
||
Ok(parse_quote!( | ||
#[automatically_derived] | ||
impl #impl_generics #trait_ident #trait_generics for std::rc::Rc<#generic_type> { | ||
#(#methods)* | ||
} | ||
)) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
mod derive { | ||
|
||
use syn::parse_quote; | ||
|
||
#[test] | ||
fn empty() { | ||
let trait_ = parse_quote!( | ||
trait Trait {} | ||
); | ||
assert_eq!( | ||
super::super::derive(&trait_).unwrap(), | ||
parse_quote!( | ||
#[automatically_derived] | ||
impl<T: Trait + ?Sized> Trait for std::rc::Rc<T> {} | ||
) | ||
); | ||
} | ||
|
||
#[test] | ||
fn receiver_ref() { | ||
let trait_ = parse_quote!( | ||
trait Trait { | ||
fn my_method(&self); | ||
} | ||
); | ||
assert_eq!( | ||
super::super::derive(&trait_).unwrap(), | ||
parse_quote!( | ||
#[automatically_derived] | ||
impl<T: Trait + ?Sized> Trait for std::rc::Rc<T> { | ||
#[inline] | ||
fn my_method(&self) { | ||
(*(*self)).my_method() | ||
} | ||
} | ||
) | ||
); | ||
} | ||
|
||
#[test] | ||
fn receiver_mut() { | ||
let trait_ = parse_quote!( | ||
trait Trait { | ||
fn my_method(&mut self); | ||
} | ||
); | ||
assert!(super::super::derive(&trait_).is_err()); | ||
} | ||
|
||
#[test] | ||
fn receiver_self() { | ||
let trait_ = parse_quote!( | ||
trait Trait { | ||
fn my_method(self); | ||
} | ||
); | ||
assert!(super::super::derive(&trait_).is_err()); | ||
} | ||
|
||
#[test] | ||
fn receiver_arbitrary() { | ||
let trait_ = parse_quote!( | ||
trait Trait { | ||
fn my_method(self: Box<Self>); | ||
} | ||
); | ||
assert!(super::super::derive(&trait_).is_err()); | ||
} | ||
|
||
#[test] | ||
fn generics() { | ||
let trait_ = parse_quote!( | ||
trait MyTrait<T> {} | ||
); | ||
let derived = super::super::derive(&trait_).unwrap(); | ||
|
||
assert_eq!( | ||
derived, | ||
parse_quote!( | ||
#[automatically_derived] | ||
impl<T, MT: MyTrait<T> + ?Sized> MyTrait<T> for std::rc::Rc<MT> {} | ||
) | ||
); | ||
} | ||
} | ||
} |
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,10 @@ | ||
extern crate blanket; | ||
|
||
use blanket::blanket; | ||
|
||
#[blanket(derive(Rc))] | ||
pub trait Counter { | ||
fn increment(self: Box<Self>); | ||
} | ||
|
||
fn main() {} |
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,5 @@ | ||
error: cannot derive `Rc` for a trait declaring methods with arbitrary receiver types | ||
--> $DIR/boxmethods.rs:7:18 | ||
| | ||
7 | fn increment(self: Box<Self>); | ||
| ^^^^^^^^^^^^^^^ |
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,10 @@ | ||
extern crate blanket; | ||
|
||
use blanket::blanket; | ||
|
||
#[blanket(derive(Rc))] | ||
pub trait Counter { | ||
fn increment(&mut self); | ||
} | ||
|
||
fn main() {} |
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,5 @@ | ||
error: cannot derive `Rc` for a trait declaring `&mut self` methods | ||
--> $DIR/mutmethods.rs:7:18 | ||
| | ||
7 | fn increment(&mut self); | ||
| ^^^^^^^^^ |
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,37 @@ | ||
use std::rc::Rc; | ||
use std::sync::atomic::AtomicU8; | ||
use std::sync::atomic::Ordering; | ||
|
||
pub trait Counter { | ||
fn increment(&self); | ||
} | ||
|
||
#[derive(Default)] | ||
struct AtomicCounter { | ||
count: AtomicU8 | ||
} | ||
|
||
impl Counter for AtomicCounter { | ||
fn increment(&self) { | ||
self.count.fetch_add(1, Ordering::SeqCst); | ||
} | ||
} | ||
|
||
struct CounterWrapper<C: Counter> { | ||
inner: C | ||
} | ||
|
||
impl<C: Counter> From<C> for CounterWrapper<C> { | ||
fn from(inner: C) -> Self { | ||
Self { inner } | ||
} | ||
} | ||
|
||
fn main() { | ||
// counter wrapper should be able to wrap AtomicCounter | ||
let counter = AtomicCounter::default(); | ||
let wrapper_by_value = CounterWrapper::from(counter); | ||
// but this will fail because no implementation was derived | ||
let counter = AtomicCounter::default(); | ||
let wrapper_by_ref = CounterWrapper::from(Rc::new(counter)); | ||
} |
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,17 @@ | ||
error[E0277]: the trait bound `std::rc::Rc<AtomicCounter>: Counter` is not satisfied | ||
--> $DIR/noderive.rs:36:47 | ||
| | ||
20 | struct CounterWrapper<C: Counter> { | ||
| ------- required by this bound in `CounterWrapper` | ||
... | ||
36 | let wrapper_by_ref = CounterWrapper::from(Rc::new(counter)); | ||
| ^^^^^^^^^^^^^^^^ the trait `Counter` is not implemented for `std::rc::Rc<AtomicCounter>` | ||
|
||
error[E0277]: the trait bound `std::rc::Rc<AtomicCounter>: Counter` is not satisfied | ||
--> $DIR/noderive.rs:36:26 | ||
| | ||
20 | struct CounterWrapper<C: Counter> { | ||
| ------- required by this bound in `CounterWrapper` | ||
... | ||
36 | let wrapper_by_ref = CounterWrapper::from(Rc::new(counter)); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Counter` is not implemented for `std::rc::Rc<AtomicCounter>` |
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,10 @@ | ||
extern crate blanket; | ||
|
||
use blanket::blanket; | ||
|
||
#[blanket(derive(Rc))] | ||
pub trait Extract { | ||
fn extract(self); | ||
} | ||
|
||
fn main() {} |
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,5 @@ | ||
error: cannot derive `Rc` for a trait declaring `self` methods | ||
--> $DIR/selfmethods.rs:7:16 | ||
| | ||
7 | fn extract(self); | ||
| ^^^^ |
Oops, something went wrong.