about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Burka <aburka@seas.upenn.edu>2016-03-08 13:24:28 -0500
committerAlex Burka <aburka@seas.upenn.edu>2016-03-14 16:59:55 -0400
commit8355389e3e9d299d90ea78197c0e5b6c2162a957 (patch)
tree6f0853fad3dbb342f9c3801d20882f19145be12e
parentfd4fa62885d7b7319a7cf88e834fa1016ac9ae5c (diff)
downloadrust-8355389e3e9d299d90ea78197c0e5b6c2162a957.tar.gz
rust-8355389e3e9d299d90ea78197c0e5b6c2162a957.zip
derive: improve hygiene for type parameters (see #2810)
When deriving Hash, RustcEncodable and RustcDecodable, the syntax extension
needs a type parameter to use in the inner method. They used to use __H, __S
and __D respectively. If this conflicts with a type parameter already declared
for the item, bad times result (see the test). There is no hygiene for type
parameters, but this commit introduces a better heuristic by concatenating the
names of all extant type parameters (and prepending __H).
-rw-r--r--src/libsyntax_ext/deriving/cmp/ord.rs2
-rw-r--r--src/libsyntax_ext/deriving/decodable.rs12
-rw-r--r--src/libsyntax_ext/deriving/encodable.rs12
-rw-r--r--src/libsyntax_ext/deriving/hash.rs8
-rw-r--r--src/libsyntax_ext/deriving/mod.rs29
-rw-r--r--src/test/run-pass/deriving-hash.rs4
6 files changed, 50 insertions, 17 deletions
diff --git a/src/libsyntax_ext/deriving/cmp/ord.rs b/src/libsyntax_ext/deriving/cmp/ord.rs
index a69d57423a2..a7e156c5f68 100644
--- a/src/libsyntax_ext/deriving/cmp/ord.rs
+++ b/src/libsyntax_ext/deriving/cmp/ord.rs
@@ -11,7 +11,7 @@
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
-use syntax::ast::{MetaItem, Expr, BinOpKind, self};
+use syntax::ast::{MetaItem, Expr, self};
 use syntax::codemap::Span;
 use syntax::ext::base::{ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
diff --git a/src/libsyntax_ext/deriving/decodable.rs b/src/libsyntax_ext/deriving/decodable.rs
index 092f8548966..49f14c937e9 100644
--- a/src/libsyntax_ext/deriving/decodable.rs
+++ b/src/libsyntax_ext/deriving/decodable.rs
@@ -10,6 +10,7 @@
 
 //! The compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more.
 
+use deriving;
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
@@ -54,6 +55,8 @@ fn expand_deriving_decodable_imp(cx: &mut ExtCtxt,
         return
     }
 
+    let typaram = &*deriving::hygienic_type_parameter(item, "__D");
+
     let trait_def = TraitDef {
         span: span,
         attributes: Vec::new(),
@@ -66,18 +69,17 @@ fn expand_deriving_decodable_imp(cx: &mut ExtCtxt,
                 name: "decode",
                 generics: LifetimeBounds {
                     lifetimes: Vec::new(),
-                    bounds: vec!(("__D", vec!(Path::new_(
-                                    vec!(krate, "Decoder"), None,
-                                    vec!(), true))))
+                    bounds: vec![(typaram,
+                                  vec![Path::new_(vec!(krate, "Decoder"), None, vec!(), true)])]
                 },
                 explicit_self: None,
-                args: vec!(Ptr(Box::new(Literal(Path::new_local("__D"))),
+                args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))),
                             Borrowed(None, Mutability::Mutable))),
                 ret_ty: Literal(Path::new_(
                     pathvec_std!(cx, core::result::Result),
                     None,
                     vec!(Box::new(Self_), Box::new(Literal(Path::new_(
-                        vec!["__D", "Error"], None, vec![], false
+                        vec![typaram, "Error"], None, vec![], false
                     )))),
                     true
                 )),
diff --git a/src/libsyntax_ext/deriving/encodable.rs b/src/libsyntax_ext/deriving/encodable.rs
index 8262a04e9ce..a05bd7869b2 100644
--- a/src/libsyntax_ext/deriving/encodable.rs
+++ b/src/libsyntax_ext/deriving/encodable.rs
@@ -88,6 +88,7 @@
 //! }
 //! ```
 
+use deriving;
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
@@ -130,6 +131,8 @@ fn expand_deriving_encodable_imp(cx: &mut ExtCtxt,
         return;
     }
 
+    let typaram = &*deriving::hygienic_type_parameter(item, "__S");
+
     let trait_def = TraitDef {
         span: span,
         attributes: Vec::new(),
@@ -142,18 +145,17 @@ fn expand_deriving_encodable_imp(cx: &mut ExtCtxt,
                 name: "encode",
                 generics: LifetimeBounds {
                     lifetimes: Vec::new(),
-                    bounds: vec!(("__S", vec!(Path::new_(
-                                    vec!(krate, "Encoder"), None,
-                                    vec!(), true))))
+                    bounds: vec![(typaram,
+                                  vec![Path::new_(vec![krate, "Encoder"], None, vec!(), true)])]
                 },
                 explicit_self: borrowed_explicit_self(),
-                args: vec!(Ptr(Box::new(Literal(Path::new_local("__S"))),
+                args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))),
                             Borrowed(None, Mutability::Mutable))),
                 ret_ty: Literal(Path::new_(
                     pathvec_std!(cx, core::result::Result),
                     None,
                     vec!(Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_(
-                        vec!["__S", "Error"], None, vec![], false
+                        vec![typaram, "Error"], None, vec![], false
                     )))),
                     true
                 )),
diff --git a/src/libsyntax_ext/deriving/hash.rs b/src/libsyntax_ext/deriving/hash.rs
index bf8aa8fb23d..ba38ebc8607 100644
--- a/src/libsyntax_ext/deriving/hash.rs
+++ b/src/libsyntax_ext/deriving/hash.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use deriving;
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
@@ -26,7 +27,10 @@ pub fn expand_deriving_hash(cx: &mut ExtCtxt,
 
     let path = Path::new_(pathvec_std!(cx, core::hash::Hash), None,
                           vec!(), true);
-    let arg = Path::new_local("__H");
+
+    let typaram = &*deriving::hygienic_type_parameter(item, "__H");
+
+    let arg = Path::new_local(typaram);
     let hash_trait_def = TraitDef {
         span: span,
         attributes: Vec::new(),
@@ -39,7 +43,7 @@ pub fn expand_deriving_hash(cx: &mut ExtCtxt,
                 name: "hash",
                 generics: LifetimeBounds {
                     lifetimes: Vec::new(),
-                    bounds: vec![("__H",
+                    bounds: vec![(typaram,
                                   vec![path_std!(cx, core::hash::Hasher)])],
                 },
                 explicit_self: borrowed_explicit_self(),
diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs
index 4e2142f1fb4..75de5c56ea1 100644
--- a/src/libsyntax_ext/deriving/mod.rs
+++ b/src/libsyntax_ext/deriving/mod.rs
@@ -9,11 +9,8 @@
 // except according to those terms.
 
 //! The compiler code necessary to implement the `#[derive]` extensions.
-//!
-//! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is
-//! the standard library, and "std" is the core library.
 
-use syntax::ast::{MetaItem, MetaItemKind};
+use syntax::ast::{MetaItem, MetaItemKind, self};
 use syntax::attr::AttrMetaMethods;
 use syntax::ext::base::{ExtCtxt, SyntaxEnv, Annotatable};
 use syntax::ext::base::{MultiDecorator, MultiItemDecorator, MultiModifier};
@@ -197,3 +194,27 @@ fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) {
                                    name, replacement));
     }
 }
+
+/// Construct a name for the inner type parameter that can't collide with any type parameters of
+/// the item. This is achieved by starting with a base and then concatenating the names of all
+/// other type parameters.
+// FIXME(aburka): use real hygiene when that becomes possible
+fn hygienic_type_parameter(item: &Annotatable, base: &str) -> String {
+    let mut typaram = String::from(base);
+    if let Annotatable::Item(ref item) = *item {
+        match item.node {
+            ast::ItemKind::Struct(_, ast::Generics { ref ty_params, .. }) |
+                ast::ItemKind::Enum(_, ast::Generics { ref ty_params, .. }) => {
+
+                for ty in ty_params.iter() {
+                    typaram.push_str(&ty.ident.name.as_str());
+                }
+            }
+
+            _ => {}
+        }
+    }
+
+    typaram
+}
+
diff --git a/src/test/run-pass/deriving-hash.rs b/src/test/run-pass/deriving-hash.rs
index 69e9816ab94..a98cfa2393f 100644
--- a/src/test/run-pass/deriving-hash.rs
+++ b/src/test/run-pass/deriving-hash.rs
@@ -20,6 +20,10 @@ struct Person {
     phone: usize,
 }
 
+// test for hygiene name collisions
+#[derive(Hash)] struct __H__H;
+#[derive(Hash)] enum Collision<__H> { __H { __H__H: __H } }
+
 fn hash<T: Hash>(t: &T) -> u64 {
     let mut s = SipHasher::new_with_keys(0, 0);
     t.hash(&mut s);