Skip to content

Commit 4117fff

Browse files
committed
Try detecting mixed derive/derivative with Clone and Copy
1 parent dcaba75 commit 4117fff

File tree

5 files changed

+74
-7
lines changed

5 files changed

+74
-7
lines changed

src/attr.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,17 @@ pub struct InputClone {
4141
bounds: Option<Vec<syn::WherePredicate>>,
4242
/// Whether the implementation should have an explicit `clone_from`.
4343
pub clone_from: bool,
44+
/// Whether the `rustc_copy_clone_marker` was found.
45+
pub rustc_copy_clone_marker: bool,
4446
}
4547

4648
#[derive(Debug, Default)]
4749
/// Represent the `derivative(Clone(…))` attributes on an input.
4850
pub struct InputCopy {
4951
/// The `bound` attribute if present and the corresponding bounds.
5052
bounds: Option<Vec<syn::WherePredicate>>,
53+
/// Wether the input also derive `Clone` (ie. `derive(Clone)`, but not `derivative(Clone)`)
54+
derives_clone: bool,
5155
}
5256

5357
#[derive(Debug, Default)]
@@ -168,21 +172,40 @@ impl Input {
168172
for_all_attr! {
169173
for (name, values) in attrs;
170174
"Clone" => {
175+
let mut clone = input.clone.take().unwrap_or_default();
176+
177+
println!("{:?}", attrs);
178+
clone.rustc_copy_clone_marker = attrs
179+
.iter()
180+
.any(|attr| attr.value.name() == "rustc_copy_clone_marker");
181+
171182
match_attributes! {
172-
let Some(clone) = input.clone;
173183
for value in values;
174184
"bound" => try!(parse_bound(&mut clone.bounds, value)),
175185
"clone_from" => {
176186
clone.clone_from = try!(parse_boolean_meta_item(&value, true, "clone_from"));
177187
}
178188
}
189+
190+
input.clone = Some(clone);
179191
}
180192
"Copy" => {
193+
let mut copy = input.copy.take().unwrap_or_default();
194+
195+
for attr in attrs {
196+
if let syn::MetaItem::List(ref name, ref traits) = attr.value {
197+
if name == "derive" && traits.iter().any(|mi| mi.name() == "Clone") {
198+
copy.derives_clone = true;
199+
}
200+
}
201+
}
202+
181203
match_attributes! {
182-
let Some(copy) = input.copy;
183204
for value in values;
184205
"bound" => try!(parse_bound(&mut copy.bounds, value)),
185206
}
207+
208+
input.copy = Some(copy);
186209
}
187210
"Debug" => {
188211
match_attributes! {
@@ -238,6 +261,10 @@ impl Input {
238261
self.copy.as_ref().map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice))
239262
}
240263

264+
pub fn derives_clone(&self) -> bool {
265+
self.copy.as_ref().map_or(false, |d| d.derives_clone)
266+
}
267+
241268
pub fn debug_bound(&self) -> Option<&[syn::WherePredicate]> {
242269
self.debug.as_ref().map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice))
243270
}
@@ -254,6 +281,10 @@ impl Input {
254281
self.eq.as_ref().map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice))
255282
}
256283

284+
pub fn rustc_copy_clone_marker(&self) -> bool {
285+
self.clone.as_ref().map_or(false, |d| d.rustc_copy_clone_marker)
286+
}
287+
257288
pub fn partial_eq_bound(&self) -> Option<&[syn::WherePredicate]> {
258289
self.partial_eq.as_ref().map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice))
259290
}

src/clone.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ use syn::{self, aster};
66
use utils;
77

88
/// Derive `Copy` for `input`.
9-
pub fn derive_copy(input: &ast::Input) -> quote::Tokens {
9+
pub fn derive_copy(input: &ast::Input) -> Result<quote::Tokens, String> {
1010
let name = &input.ident;
1111

12+
if input.attrs.derives_clone() {
13+
return Err("`#[derivative(Copy)]` can't be used with `#[derive(Clone)]`".into());
14+
}
15+
1216
let copy_trait_path = copy_trait_path();
1317
let impl_generics = utils::build_impl_generics(
1418
input,
@@ -25,10 +29,10 @@ pub fn derive_copy(input: &ast::Input) -> quote::Tokens {
2529
.build()
2630
.build();
2731

28-
quote! {
32+
Ok(quote! {
2933
#[allow(unused_qualifications)]
3034
impl #impl_generics #copy_trait_path for #ty #where_clause {}
31-
}
35+
})
3236
}
3337

3438
/// Derive `Clone` for `input`.
@@ -51,7 +55,8 @@ pub fn derive_clone(input: &ast::Input) -> quote::Tokens {
5155
.build()
5256
.build();
5357

54-
if input.attrs.copy.is_some() && input.generics.ty_params.is_empty() {
58+
let is_copy = input.attrs.rustc_copy_clone_marker() || input.attrs.copy.is_some();
59+
if is_copy && input.generics.ty_params.is_empty() {
5560
quote! {
5661
#[allow(unused_qualifications)]
5762
impl #impl_generics #clone_trait_path for #ty #where_clause {

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ fn derive_impls(input: &ast::Input) -> Result<quote::Tokens, String> {
2525
tokens.append(&clone::derive_clone(input).to_string());
2626
}
2727
if input.attrs.copy.is_some() {
28-
tokens.append(&clone::derive_copy(input).to_string());
28+
tokens.append(&try!(clone::derive_copy(input)).to_string());
2929
}
3030
if input.attrs.debug.is_some() {
3131
tokens.append(&debug::derive(input).to_string());
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![feature(proc_macro)]
2+
3+
#[macro_use]
4+
extern crate derivative;
5+
6+
#[derive(Derivative)]
7+
//~^ ERROR custom derive attribute panicked
8+
//~| HELP `#[derivative(Copy)]` can't be used with `#[derive(Clone)]`
9+
#[derivative(Copy)]
10+
#[derive(Clone)]
11+
struct OurTheir1;
12+
13+
fn main() {
14+
}

tests/run-pass/rustc-deriving-copyclone.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ struct OurOur1(Liar);
4646
#[derivative(Clone, Copy)]
4747
struct OurOur2(Liar);
4848

49+
#[derive(Copy)]
50+
#[derive(Derivative)]
51+
#[derivative(Clone)]
52+
struct TheirOur1(Liar);
53+
#[derive(Copy)]
54+
#[derive(Derivative)]
55+
#[derivative(Clone)]
56+
struct TheirOur2(Liar);
57+
4958
fn main() {
5059
let _ = TheirTheir(Liar).clone();
5160
assert!(!CLONED.load(Ordering::SeqCst), "TheirTheir");
@@ -54,4 +63,12 @@ fn main() {
5463
assert!(!CLONED.load(Ordering::SeqCst), "OurOur1");
5564
let _ = OurOur2(Liar).clone();
5665
assert!(!CLONED.load(Ordering::SeqCst), "OurOur2");
66+
67+
// Ideally this would work the same, just testing that the behaviour does not change:
68+
CLONED.store(false, Ordering::SeqCst);
69+
let _ = TheirOur1(Liar).clone();
70+
assert!(CLONED.load(Ordering::SeqCst), "TheirOur1");
71+
CLONED.store(false, Ordering::SeqCst);
72+
let _ = TheirOur2(Liar).clone();
73+
assert!(CLONED.load(Ordering::SeqCst), "TheirOur2");
5774
}

0 commit comments

Comments
 (0)