summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorErick Tryzelaar <erick.tryzelaar@gmail.com>2013-02-27 11:03:21 -0800
committerErick Tryzelaar <erick.tryzelaar@gmail.com>2013-02-27 11:03:21 -0800
commit7d0ec86c4a382f3d937c4bf5f12ab69468d991c1 (patch)
tree8a793aa83482c45a0aaae05f1bfac087ecee0b59 /src/libsyntax
parentea36a0dee1630e24ba2889ca13550026b1af4f9d (diff)
parenta6d9689399d091c3265f00434a69c551a61c28dc (diff)
downloadrust-7d0ec86c4a382f3d937c4bf5f12ab69468d991c1.tar.gz
rust-7d0ec86c4a382f3d937c4bf5f12ab69468d991c1.zip
Merge remote-tracking branch 'remotes/origin/incoming' into incoming
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs80
-rw-r--r--src/libsyntax/ast_map.rs2
-rw-r--r--src/libsyntax/ast_util.rs4
-rw-r--r--src/libsyntax/codemap.rs30
-rw-r--r--src/libsyntax/ext/auto_encode.rs46
-rw-r--r--src/libsyntax/ext/base.rs221
-rw-r--r--src/libsyntax/ext/expand.rs318
-rw-r--r--src/libsyntax/ext/pipes/ast_builder.rs4
-rw-r--r--src/libsyntax/ext/pipes/check.rs2
-rw-r--r--src/libsyntax/ext/pipes/parse_proto.rs2
-rw-r--r--src/libsyntax/ext/pipes/pipec.rs6
-rw-r--r--src/libsyntax/ext/pipes/proto.rs2
-rw-r--r--src/libsyntax/ext/source_util.rs57
-rw-r--r--src/libsyntax/fold.rs2
-rw-r--r--src/libsyntax/parse/lexer.rs50
-rw-r--r--src/libsyntax/parse/mod.rs36
-rw-r--r--src/libsyntax/parse/obsolete.rs23
-rw-r--r--src/libsyntax/parse/parser.rs92
-rw-r--r--src/libsyntax/parse/token.rs4
19 files changed, 739 insertions, 242 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 4d071e4b26f..d99dcd53c39 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -29,10 +29,39 @@ macro_rules! interner_key (
         (-3 as uint, 0u)))
 )
 
+// an identifier contains an index into the interner
+// table and a SyntaxContext to track renaming and
+// macro expansion per Flatt et al., "Macros
+// That Work Together"
 #[deriving_eq]
-pub struct ident { repr: uint }
+pub struct ident { repr: Name }
 
-pub impl<S:Encoder> Encodable<S> for ident {
+// a SyntaxContext represents a chain of macro-expandings
+// and renamings. Each macro expansion corresponds to
+// a fresh uint
+#[deriving_eq]
+pub enum SyntaxContext {
+    MT,
+    Mark (Mrk,~SyntaxContext),
+    Rename (~ident,Name,~SyntaxContext)
+}
+
+/*
+// ** this is going to have to apply to paths, not to idents.
+// Returns true if these two identifiers access the same
+// local binding or top-level binding... that's what it
+// should do. For now, it just compares the names.
+pub fn free_ident_eq (a : ident, b: ident) -> bool{
+    a.repr == b.repr
+}
+*/
+// a name represents a string, interned
+type Name = uint;
+// a mark represents a unique id associated
+// with a macro expansion
+type Mrk = uint;
+
+impl<S:Encoder> Encodable<S> for ident {
     fn encode(&self, s: &S) {
         let intr = match unsafe {
             task::local_data::local_data_get(interner_key!())
@@ -45,7 +74,7 @@ pub impl<S:Encoder> Encodable<S> for ident {
     }
 }
 
-pub impl<D:Decoder> Decodable<D> for ident {
+impl<D:Decoder> Decodable<D> for ident {
     static fn decode(d: &D) -> ident {
         let intr = match unsafe {
             task::local_data::local_data_get(interner_key!())
@@ -58,7 +87,7 @@ pub impl<D:Decoder> Decodable<D> for ident {
     }
 }
 
-pub impl to_bytes::IterBytes for ident {
+impl to_bytes::IterBytes for ident {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         self.repr.iter_bytes(lsb0, f)
     }
@@ -217,7 +246,7 @@ pub enum binding_mode {
     bind_infer
 }
 
-pub impl to_bytes::IterBytes for binding_mode {
+impl to_bytes::IterBytes for binding_mode {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         match *self {
           bind_by_copy => 0u8.iter_bytes(lsb0, f),
@@ -262,7 +291,7 @@ pub enum pat_ {
 #[deriving_eq]
 pub enum mutability { m_mutbl, m_imm, m_const, }
 
-pub impl to_bytes::IterBytes for mutability {
+impl to_bytes::IterBytes for mutability {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as u8).iter_bytes(lsb0, f)
     }
@@ -275,13 +304,13 @@ pub enum Abi {
     RustAbi
 }
 
-pub impl to_bytes::IterBytes for Abi {
+impl to_bytes::IterBytes for Abi {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as uint).iter_bytes(lsb0, f)
     }
 }
 
-pub impl ToStr for Abi {
+impl ToStr for Abi {
     pure fn to_str(&self) -> ~str {
         match *self {
             RustAbi => ~"\"rust\""
@@ -298,13 +327,13 @@ pub enum Sigil {
     ManagedSigil
 }
 
-pub impl to_bytes::IterBytes for Sigil {
+impl to_bytes::IterBytes for Sigil {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as uint).iter_bytes(lsb0, f)
     }
 }
 
-pub impl ToStr for Sigil {
+impl ToStr for Sigil {
     pure fn to_str(&self) -> ~str {
         match *self {
             BorrowedSigil => ~"&",
@@ -383,7 +412,7 @@ pub enum inferable<T> {
     infer(node_id)
 }
 
-pub impl<T:to_bytes::IterBytes> to_bytes::IterBytes for inferable<T> {
+impl<T:to_bytes::IterBytes> to_bytes::IterBytes for inferable<T> {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         match *self {
           expl(ref t) =>
@@ -401,7 +430,7 @@ pub impl<T:to_bytes::IterBytes> to_bytes::IterBytes for inferable<T> {
 #[deriving_eq]
 pub enum rmode { by_ref, by_val, by_copy }
 
-pub impl to_bytes::IterBytes for rmode {
+impl to_bytes::IterBytes for rmode {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as u8).iter_bytes(lsb0, f)
     }
@@ -742,13 +771,13 @@ pub enum trait_method {
 #[deriving_eq]
 pub enum int_ty { ty_i, ty_char, ty_i8, ty_i16, ty_i32, ty_i64, }
 
-pub impl ToStr for int_ty {
+impl ToStr for int_ty {
     pure fn to_str(&self) -> ~str {
         ::ast_util::int_ty_to_str(*self)
     }
 }
 
-pub impl to_bytes::IterBytes for int_ty {
+impl to_bytes::IterBytes for int_ty {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as u8).iter_bytes(lsb0, f)
     }
@@ -759,13 +788,13 @@ pub impl to_bytes::IterBytes for int_ty {
 #[deriving_eq]
 pub enum uint_ty { ty_u, ty_u8, ty_u16, ty_u32, ty_u64, }
 
-pub impl ToStr for uint_ty {
+impl ToStr for uint_ty {
     pure fn to_str(&self) -> ~str {
         ::ast_util::uint_ty_to_str(*self)
     }
 }
 
-pub impl to_bytes::IterBytes for uint_ty {
+impl to_bytes::IterBytes for uint_ty {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as u8).iter_bytes(lsb0, f)
     }
@@ -776,13 +805,13 @@ pub impl to_bytes::IterBytes for uint_ty {
 #[deriving_eq]
 pub enum float_ty { ty_f, ty_f32, ty_f64, }
 
-pub impl ToStr for float_ty {
+impl ToStr for float_ty {
     pure fn to_str(&self) -> ~str {
         ::ast_util::float_ty_to_str(*self)
     }
 }
 
-pub impl to_bytes::IterBytes for float_ty {
+impl to_bytes::IterBytes for float_ty {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as u8).iter_bytes(lsb0, f)
     }
@@ -836,7 +865,7 @@ pub enum Onceness {
     Many
 }
 
-pub impl ToStr for Onceness {
+impl ToStr for Onceness {
     pure fn to_str(&self) -> ~str {
         match *self {
             Once => ~"once",
@@ -845,7 +874,7 @@ pub impl ToStr for Onceness {
     }
 }
 
-pub impl to_bytes::IterBytes for Onceness {
+impl to_bytes::IterBytes for Onceness {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as uint).iter_bytes(lsb0, f);
     }
@@ -895,7 +924,7 @@ pub enum ty_ {
     ty_infer,
 }
 
-pub impl to_bytes::IterBytes for Ty {
+impl to_bytes::IterBytes for Ty {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         to_bytes::iter_bytes_2(&self.span.lo, &self.span.hi, lsb0, f);
     }
@@ -931,7 +960,7 @@ pub enum purity {
     extern_fn, // declared with "extern fn"
 }
 
-pub impl ToStr for purity {
+impl ToStr for purity {
     pure fn to_str(&self) -> ~str {
         match *self {
             impure_fn => ~"impure",
@@ -942,7 +971,7 @@ pub impl ToStr for purity {
     }
 }
 
-pub impl to_bytes::IterBytes for purity {
+impl to_bytes::IterBytes for purity {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as u8).iter_bytes(lsb0, f)
     }
@@ -957,7 +986,7 @@ pub enum ret_style {
     return_val, // everything else
 }
 
-pub impl to_bytes::IterBytes for ret_style {
+impl to_bytes::IterBytes for ret_style {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as u8).iter_bytes(lsb0, f)
     }
@@ -1230,6 +1259,7 @@ pub enum item_ {
               Option<@trait_ref>, // (optional) trait this impl implements
               @Ty, // self
               ~[@method]),
+    // a macro invocation (which includes macro definition)
     item_mac(mac),
 }
 
@@ -1238,7 +1268,7 @@ pub enum item_ {
 #[deriving_eq]
 pub enum struct_mutability { struct_mutable, struct_immutable }
 
-pub impl to_bytes::IterBytes for struct_mutability {
+impl to_bytes::IterBytes for struct_mutability {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as u8).iter_bytes(lsb0, f)
     }
diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs
index 48fe0ca5b2d..c1ca329335d 100644
--- a/src/libsyntax/ast_map.rs
+++ b/src/libsyntax/ast_map.rs
@@ -34,7 +34,7 @@ pub enum path_elt {
     path_name(ident)
 }
 
-pub impl cmp::Eq for path_elt {
+impl cmp::Eq for path_elt {
     pure fn eq(&self, other: &path_elt) -> bool {
         match (*self) {
             path_mod(e0a) => {
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 59f25024c82..2eac626c2e6 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -198,7 +198,7 @@ pub pure fn is_call_expr(e: @expr) -> bool {
 }
 
 // This makes def_id hashable
-pub impl to_bytes::IterBytes for def_id {
+impl to_bytes::IterBytes for def_id {
     #[inline(always)]
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         to_bytes::iter_bytes_2(&self.crate, &self.node, lsb0, f);
@@ -303,7 +303,7 @@ pub trait inlined_item_utils {
     fn accept<E>(&self, e: E, v: visit::vt<E>);
 }
 
-pub impl inlined_item_utils for inlined_item {
+impl inlined_item_utils for inlined_item {
     fn ident(&self) -> ident {
         match *self {
             ii_item(i) => /* FIXME (#2543) */ copy i.ident,
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index 1ab55fe9035..65711d9894a 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -46,71 +46,71 @@ pub enum CharPos = uint;
 // XXX: Lots of boilerplate in these impls, but so far my attempts to fix
 // have been unsuccessful
 
-pub impl Pos for BytePos {
+impl Pos for BytePos {
     static pure fn from_uint(n: uint) -> BytePos { BytePos(n) }
     pure fn to_uint(&self) -> uint { **self }
 }
 
-pub impl cmp::Eq for BytePos {
+impl cmp::Eq for BytePos {
     pure fn eq(&self, other: &BytePos) -> bool { **self == **other }
     pure fn ne(&self, other: &BytePos) -> bool { !(*self).eq(other) }
 }
 
-pub impl cmp::Ord for BytePos {
+impl cmp::Ord for BytePos {
     pure fn lt(&self, other: &BytePos) -> bool { **self < **other }
     pure fn le(&self, other: &BytePos) -> bool { **self <= **other }
     pure fn ge(&self, other: &BytePos) -> bool { **self >= **other }
     pure fn gt(&self, other: &BytePos) -> bool { **self > **other }
 }
 
-pub impl Add<BytePos, BytePos> for BytePos {
+impl Add<BytePos, BytePos> for BytePos {
     pure fn add(&self, rhs: &BytePos) -> BytePos {
         BytePos(**self + **rhs)
     }
 }
 
-pub impl Sub<BytePos, BytePos> for BytePos {
+impl Sub<BytePos, BytePos> for BytePos {
     pure fn sub(&self, rhs: &BytePos) -> BytePos {
         BytePos(**self - **rhs)
     }
 }
 
-pub impl to_bytes::IterBytes for BytePos {
+impl to_bytes::IterBytes for BytePos {
     pure fn iter_bytes(&self, +lsb0: bool, &&f: to_bytes::Cb) {
         (**self).iter_bytes(lsb0, f)
     }
 }
 
-pub impl Pos for CharPos {
+impl Pos for CharPos {
     static pure fn from_uint(n: uint) -> CharPos { CharPos(n) }
     pure fn to_uint(&self) -> uint { **self }
 }
 
-pub impl cmp::Eq for CharPos {
+impl cmp::Eq for CharPos {
     pure fn eq(&self, other: &CharPos) -> bool { **self == **other }
     pure fn ne(&self, other: &CharPos) -> bool { !(*self).eq(other) }
 }
 
-pub impl cmp::Ord for CharPos {
+impl cmp::Ord for CharPos {
     pure fn lt(&self, other: &CharPos) -> bool { **self < **other }
     pure fn le(&self, other: &CharPos) -> bool { **self <= **other }
     pure fn ge(&self, other: &CharPos) -> bool { **self >= **other }
     pure fn gt(&self, other: &CharPos) -> bool { **self > **other }
 }
 
-pub impl to_bytes::IterBytes for CharPos {
+impl to_bytes::IterBytes for CharPos {
     pure fn iter_bytes(&self, +lsb0: bool, &&f: to_bytes::Cb) {
         (**self).iter_bytes(lsb0, f)
     }
 }
 
-pub impl Add<CharPos,CharPos> for CharPos {
+impl Add<CharPos,CharPos> for CharPos {
     pure fn add(&self, rhs: &CharPos) -> CharPos {
         CharPos(**self + **rhs)
     }
 }
 
-pub impl Sub<CharPos,CharPos> for CharPos {
+impl Sub<CharPos,CharPos> for CharPos {
     pure fn sub(&self, rhs: &CharPos) -> CharPos {
         CharPos(**self - **rhs)
     }
@@ -133,19 +133,19 @@ pub struct span {
 #[deriving_eq]
 pub struct spanned<T> { node: T, span: span }
 
-pub impl cmp::Eq for span {
+impl cmp::Eq for span {
     pure fn eq(&self, other: &span) -> bool {
         return (*self).lo == (*other).lo && (*self).hi == (*other).hi;
     }
     pure fn ne(&self, other: &span) -> bool { !(*self).eq(other) }
 }
 
-pub impl<S:Encoder> Encodable<S> for span {
+impl<S:Encoder> Encodable<S> for span {
     /* Note #1972 -- spans are encoded but not decoded */
     fn encode(&self, _s: &S) { }
 }
 
-pub impl<D:Decoder> Decodable<D> for span {
+impl<D:Decoder> Decodable<D> for span {
     static fn decode(_d: &D) -> span {
         dummy_sp()
     }
diff --git a/src/libsyntax/ext/auto_encode.rs b/src/libsyntax/ext/auto_encode.rs
index ea8678ed208..2addd365ff9 100644
--- a/src/libsyntax/ext/auto_encode.rs
+++ b/src/libsyntax/ext/auto_encode.rs
@@ -1188,12 +1188,14 @@ mod test {
         CallToEmitEnumVariantArg(uint),
         CallToEmitUint(uint),
         CallToEmitNil,
+        CallToEmitStruct(~str,uint),
+        CallToEmitField(~str,uint),
         // all of the ones I was too lazy to handle:
         CallToOther
     }
-    // using a mutable field rather than changing the
+    // using `@mut` rather than changing the
     // type of self in every method of every encoder everywhere.
-    pub struct TestEncoder {mut call_log : ~[call]}
+    pub struct TestEncoder {call_log : @mut ~[call]}
 
     pub impl TestEncoder {
         // these self's should be &mut self's, as well....
@@ -1205,7 +1207,7 @@ mod test {
         }
     }
 
-    pub impl Encoder for TestEncoder {
+    impl Encoder for TestEncoder {
         fn emit_nil(&self) { self.add_to_log(CallToEmitNil) }
 
         fn emit_uint(&self, +v: uint) {self.add_to_log(CallToEmitUint(v)); }
@@ -1266,11 +1268,11 @@ mod test {
         fn emit_rec(&self, f: fn()) {
             self.add_unknown_to_log(); f();
         }
-        fn emit_struct(&self, _name: &str, +_len: uint, f: fn()) {
-            self.add_unknown_to_log(); f();
+        fn emit_struct(&self, name: &str, +len: uint, f: fn()) {
+            self.add_to_log(CallToEmitStruct (name.to_str(),len)); f();
         }
-        fn emit_field(&self, _name: &str, +_idx: uint, f: fn()) {
-            self.add_unknown_to_log(); f();
+        fn emit_field(&self, name: &str, +idx: uint, f: fn()) {
+            self.add_to_log(CallToEmitField (name.to_str(),idx)); f();
         }
 
         fn emit_tup(&self, +_len: uint, f: fn()) {
@@ -1282,23 +1284,12 @@ mod test {
     }
 
 
-    #[auto_decode]
-    #[auto_encode]
-    struct Node {id: uint}
-
     fn to_call_log (val: Encodable<TestEncoder>) -> ~[call] {
-        let mut te = TestEncoder {call_log: ~[]};
+        let mut te = TestEncoder {call_log: @mut ~[]};
         val.encode(&te);
-        te.call_log
-    }
-/*
-    #[test] fn encode_test () {
-        check_equal (to_call_log(Node{id:34}
-                                 as Encodable::<std::json::Encoder>),
-                     ~[CallToEnum (~"Node"),
-                       CallToEnumVariant]);
+        copy *te.call_log
     }
-*/
+
     #[auto_encode]
     enum Written {
         Book(uint,uint),
@@ -1315,4 +1306,17 @@ mod test {
                        CallToEmitEnumVariantArg (1),
                        CallToEmitUint (44)]);
         }
+
+    pub enum BPos = uint;
+
+    #[auto_encode]
+    pub struct HasPos { pos : BPos }
+
+    #[test] fn encode_newtype_test () {
+        check_equal (to_call_log (HasPos {pos:BPos(48)}
+                                 as Encodable::<TestEncoder>),
+                    ~[CallToEmitStruct(~"HasPos",1),
+                      CallToEmitField(~"pos",0),
+                      CallToEmitUint(48)]);
+    }
 }
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 9d597b539bb..f5a7bbddf99 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -21,12 +21,12 @@ use parse::{parser, token};
 
 use core::io;
 use core::vec;
-use std::oldmap::HashMap;
+use core::hashmap::linear::LinearMap;
 
 // new-style macro! tt code:
 //
 //    SyntaxExpanderTT, SyntaxExpanderTTItem, MacResult,
-//    NormalTT, ItemTT
+//    NormalTT, IdentTT
 //
 // also note that ast::mac used to have a bunch of extraneous cases and
 // is now probably a redundant AST node, can be merged with
@@ -71,25 +71,55 @@ pub enum SyntaxExtension {
     // Token-tree expanders
     NormalTT(SyntaxExpanderTT),
 
+    // An IdentTT is a macro that has an
+    // identifier in between the name of the
+    // macro and the argument. Currently,
+    // the only examples of this are
+    // macro_rules! and proto!
+
     // perhaps macro_rules! will lose its odd special identifier argument,
     // and this can go away also
-    ItemTT(SyntaxExpanderTTItem),
+    IdentTT(SyntaxExpanderTTItem),
 }
 
-type SyntaxExtensions = HashMap<@~str, SyntaxExtension>;
+type SyntaxEnv = @mut MapChain<Name, Transformer>;
+
+// Name : the domain of SyntaxEnvs
+// want to change these to uints....
+// note that we use certain strings that are not legal as identifiers
+// to indicate, for instance, how blocks are supposed to behave.
+type Name = @~str;
+
+// Transformer : the codomain of SyntaxEnvs
+
+// NB: it may seem crazy to lump both of these into one environment;
+// what would it mean to bind "foo" to BlockLimit(true)? The idea
+// is that this follows the lead of MTWT, and accommodates growth
+// toward a more uniform syntax syntax (sorry) where blocks are just
+// another kind of transformer.
+
+enum Transformer {
+    // this identifier maps to a syntax extension or macro
+    SE(SyntaxExtension),
+    // should blocks occurring here limit macro scopes?
+    ScopeMacros(bool)
+}
 
-// A temporary hard-coded map of methods for expanding syntax extension
+// The base map of methods for expanding syntax extension
 // AST nodes into full ASTs
-pub fn syntax_expander_table() -> SyntaxExtensions {
+pub fn syntax_expander_table() -> SyntaxEnv {
     // utility function to simplify creating NormalTT syntax extensions
-    fn builtin_normal_tt(f: SyntaxExpanderTTFun) -> SyntaxExtension {
-        NormalTT(SyntaxExpanderTT{expander: f, span: None})
+    fn builtin_normal_tt(f: SyntaxExpanderTTFun) -> @Transformer {
+        @SE(NormalTT(SyntaxExpanderTT{expander: f, span: None}))
     }
-    // utility function to simplify creating ItemTT syntax extensions
-    fn builtin_item_tt(f: SyntaxExpanderTTItemFun) -> SyntaxExtension {
-        ItemTT(SyntaxExpanderTTItem{expander: f, span: None})
+    // utility function to simplify creating IdentTT syntax extensions
+    fn builtin_item_tt(f: SyntaxExpanderTTItemFun) -> @Transformer {
+        @SE(IdentTT(SyntaxExpanderTTItem{expander: f, span: None}))
     }
-    let syntax_expanders = HashMap();
+    let mut syntax_expanders = LinearMap::new();
+    // NB identifier starts with space, and can't conflict with legal idents
+    syntax_expanders.insert(@~" block",
+                            @ScopeMacros(true));
     syntax_expanders.insert(@~"macro_rules",
                             builtin_item_tt(
                                 ext::tt::macro_rules::add_new_extension));
@@ -97,10 +127,10 @@ pub fn syntax_expander_table() -> SyntaxExtensions {
                             builtin_normal_tt(ext::fmt::expand_syntax_ext));
     syntax_expanders.insert(
         @~"auto_encode",
-        ItemDecorator(ext::auto_encode::expand_auto_encode));
+        @SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
     syntax_expanders.insert(
         @~"auto_decode",
-        ItemDecorator(ext::auto_encode::expand_auto_decode));
+        @SE(ItemDecorator(ext::auto_encode::expand_auto_decode)));
     syntax_expanders.insert(@~"env",
                             builtin_normal_tt(ext::env::expand_syntax_ext));
     syntax_expanders.insert(@~"concat_idents",
@@ -110,25 +140,25 @@ pub fn syntax_expander_table() -> SyntaxExtensions {
                             builtin_normal_tt(
                                 ext::log_syntax::expand_syntax_ext));
     syntax_expanders.insert(@~"deriving_eq",
-                            ItemDecorator(
-                                ext::deriving::expand_deriving_eq));
+                            @SE(ItemDecorator(
+                                ext::deriving::expand_deriving_eq)));
     syntax_expanders.insert(@~"deriving_iter_bytes",
-                            ItemDecorator(
-                                ext::deriving::expand_deriving_iter_bytes));
+                            @SE(ItemDecorator(
+                                ext::deriving::expand_deriving_iter_bytes)));
 
     // Quasi-quoting expanders
     syntax_expanders.insert(@~"quote_tokens",
                        builtin_normal_tt(ext::quote::expand_quote_tokens));
     syntax_expanders.insert(@~"quote_expr",
-                            builtin_normal_tt(ext::quote::expand_quote_expr));
+                       builtin_normal_tt(ext::quote::expand_quote_expr));
     syntax_expanders.insert(@~"quote_ty",
-                            builtin_normal_tt(ext::quote::expand_quote_ty));
+                       builtin_normal_tt(ext::quote::expand_quote_ty));
     syntax_expanders.insert(@~"quote_item",
-                            builtin_normal_tt(ext::quote::expand_quote_item));
+                       builtin_normal_tt(ext::quote::expand_quote_item));
     syntax_expanders.insert(@~"quote_pat",
-                            builtin_normal_tt(ext::quote::expand_quote_pat));
+                       builtin_normal_tt(ext::quote::expand_quote_pat));
     syntax_expanders.insert(@~"quote_stmt",
-                            builtin_normal_tt(ext::quote::expand_quote_stmt));
+                       builtin_normal_tt(ext::quote::expand_quote_stmt));
 
     syntax_expanders.insert(@~"line",
                             builtin_normal_tt(
@@ -159,7 +189,7 @@ pub fn syntax_expander_table() -> SyntaxExtensions {
     syntax_expanders.insert(
         @~"trace_macros",
         builtin_normal_tt(ext::trace_macros::expand_trace_macros));
-    return syntax_expanders;
+    MapChain::new(~syntax_expanders)
 }
 
 // One of these is made during expansion and incrementally updated as we go;
@@ -347,6 +377,149 @@ pub fn get_exprs_from_tts(cx: ext_ctxt, tts: &[ast::token_tree])
     es
 }
 
+// in order to have some notion of scoping for macros,
+// we want to implement the notion of a transformation
+// environment.
+
+// This environment maps Names to Transformers.
+// Initially, this includes macro definitions and
+// block directives.
+
+
+
+// Actually, the following implementation is parameterized
+// by both key and value types.
+
+//impl question: how to implement it? Initially, the
+// env will contain only macros, so it might be painful
+// to add an empty frame for every context. Let's just
+// get it working, first....
+
+// NB! the mutability of the underlying maps means that
+// if expansion is out-of-order, a deeper scope may be
+// able to refer to a macro that was added to an enclosing
+// scope lexically later than the deeper scope.
+
+// Note on choice of representation: I've been pushed to
+// use a top-level managed pointer by some difficulties
+// with pushing and popping functionally, and the ownership
+// issues.  As a result, the values returned by the table
+// also need to be managed; the &self/... type that Maps
+// return won't work for things that need to get outside
+// of that managed pointer.  The easiest way to do this
+// is just to insist that the values in the tables are
+// managed to begin with.
+
+// a transformer env is either a base map or a map on top
+// of another chain.
+pub enum MapChain<K,V> {
+    BaseMapChain(~LinearMap<K,@V>),
+    ConsMapChain(~LinearMap<K,@V>,@mut MapChain<K,V>)
+}
+
+
+// get the map from an env frame
+impl <K: Eq + Hash + IterBytes ,V: Copy> MapChain<K,V>{
+
+    // Constructor. I don't think we need a zero-arg one.
+    static fn new(+init: ~LinearMap<K,@V>) -> @mut MapChain<K,V> {
+        @mut BaseMapChain(init)
+    }
+
+    // add a new frame to the environment (functionally)
+    fn push_frame (@mut self) -> @mut MapChain<K,V> {
+        @mut ConsMapChain(~LinearMap::new() ,self)
+    }
+
+// no need for pop, it'll just be functional.
+
+    // utility fn...
+
+    // ugh: can't get this to compile with mut because of the
+    // lack of flow sensitivity.
+    fn get_map(&self) -> &self/LinearMap<K,@V> {
+        match *self {
+            BaseMapChain (~ref map) => map,
+            ConsMapChain (~ref map,_) => map
+        }
+    }
+
+// traits just don't work anywhere...?
+//pub impl Map<Name,SyntaxExtension> for MapChain {
+
+    pure fn contains_key (&self, key: &K) -> bool {
+        match *self {
+            BaseMapChain (ref map) => map.contains_key(key),
+            ConsMapChain (ref map,ref rest) =>
+            (map.contains_key(key)
+             || rest.contains_key(key))
+        }
+    }
+    // should each_key and each_value operate on shadowed
+    // names? I think not.
+    // delaying implementing this....
+    pure fn each_key (&self, _f: &fn (&K)->bool) {
+        fail!(~"unimplemented 2013-02-15T10:01");
+    }
+
+    pure fn each_value (&self, _f: &fn (&V) -> bool) {
+        fail!(~"unimplemented 2013-02-15T10:02");
+    }
+
+    // Returns a copy of the value that the name maps to.
+    // Goes down the chain 'til it finds one (or bottom out).
+    fn find (&self, key: &K) -> Option<@V> {
+        match self.get_map().find (key) {
+            Some(ref v) => Some(**v),
+            None => match *self {
+                BaseMapChain (_) => None,
+                ConsMapChain (_,ref rest) => rest.find(key)
+            }
+        }
+    }
+
+    // insert the binding into the top-level map
+    fn insert (&mut self, +key: K, +ext: @V) -> bool {
+        // can't abstract over get_map because of flow sensitivity...
+        match *self {
+            BaseMapChain (~ref mut map) => map.insert(key, ext),
+            ConsMapChain (~ref mut map,_) => map.insert(key,ext)
+        }
+    }
+
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use super::MapChain;
+    use util::testing::check_equal;
+
+    #[test] fn testenv () {
+        let mut a = LinearMap::new();
+        a.insert (@~"abc",@15);
+        let m = MapChain::new(~a);
+        m.insert (@~"def",@16);
+        // FIXME: #4492 (ICE)  check_equal(m.find(&@~"abc"),Some(@15));
+        //  ....               check_equal(m.find(&@~"def"),Some(@16));
+        check_equal(*(m.find(&@~"abc").get()),15);
+        check_equal(*(m.find(&@~"def").get()),16);
+        let n = m.push_frame();
+        // old bindings are still present:
+        check_equal(*(n.find(&@~"abc").get()),15);
+        check_equal(*(n.find(&@~"def").get()),16);
+        n.insert (@~"def",@17);
+        // n shows the new binding
+        check_equal(*(n.find(&@~"abc").get()),15);
+        check_equal(*(n.find(&@~"def").get()),17);
+        // ... but m still has the old ones
+        // FIXME: #4492: check_equal(m.find(&@~"abc"),Some(@15));
+        // FIXME: #4492: check_equal(m.find(&@~"def"),Some(@16));
+        check_equal(*(m.find(&@~"abc").get()),15);
+        check_equal(*(m.find(&@~"def").get()),16);
+    }
+}
+
 //
 // Local Variables:
 // mode: rust
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 282506929ff..14f4cd5c19f 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -17,13 +17,13 @@ use attr;
 use codemap::{span, CallInfo, ExpandedFrom, NameAndSpan};
 use ext::base::*;
 use fold::*;
-use parse::{parser, parse_expr_from_source_str, new_parser_from_tts};
+use parse::{parser, parse_item_from_source_str, new_parser_from_tts};
 
 use core::option;
 use core::vec;
-use std::oldmap::HashMap;
+use core::hashmap::LinearMap;
 
-pub fn expand_expr(exts: SyntaxExtensions, cx: ext_ctxt,
+pub fn expand_expr(extsbox: @mut SyntaxEnv, cx: ext_ctxt,
                    e: &expr_, s: span, fld: ast_fold,
                    orig: fn@(&expr_, span, ast_fold) -> (expr_, span))
                 -> (expr_, span) {
@@ -39,16 +39,17 @@ pub fn expand_expr(exts: SyntaxExtensions, cx: ext_ctxt,
                     /* using idents and token::special_idents would make the
                     the macro names be hygienic */
                     let extname = cx.parse_sess().interner.get(pth.idents[0]);
-                    match exts.find(&extname) {
+                    // leaving explicit deref here to highlight unbox op:
+                    match (*extsbox).find(&extname) {
                         None => {
                             cx.span_fatal(
                                 pth.span,
                                 fmt!("macro undefined: '%s'", *extname))
                         }
-                        Some(NormalTT(SyntaxExpanderTT{
+                        Some(@SE(NormalTT(SyntaxExpanderTT{
                             expander: exp,
                             span: exp_sp
-                        })) => {
+                        }))) => {
                             cx.bt_push(ExpandedFrom(CallInfo {
                                 call_site: s,
                                 callee: NameAndSpan {
@@ -101,7 +102,7 @@ pub fn expand_expr(exts: SyntaxExtensions, cx: ext_ctxt,
 //
 // NB: there is some redundancy between this and expand_item, below, and
 // they might benefit from some amount of semantic and language-UI merger.
-pub fn expand_mod_items(exts: SyntaxExtensions, cx: ext_ctxt,
+pub fn expand_mod_items(extsbox: @mut SyntaxEnv, cx: ext_ctxt,
                         module_: &ast::_mod, fld: ast_fold,
                         orig: fn@(&ast::_mod, ast_fold) -> ast::_mod)
                      -> ast::_mod {
@@ -115,9 +116,8 @@ pub fn expand_mod_items(exts: SyntaxExtensions, cx: ext_ctxt,
         do vec::foldr(item.attrs, ~[*item]) |attr, items| {
             let mname = attr::get_attr_name(attr);
 
-            match exts.find(&mname) {
-              None | Some(NormalTT(_)) | Some(ItemTT(*)) => items,
-              Some(ItemDecorator(dec_fn)) => {
+            match (*extsbox).find(&mname) {
+              Some(@SE(ItemDecorator(dec_fn))) => {
                   cx.bt_push(ExpandedFrom(CallInfo {
                       call_site: attr.span,
                       callee: NameAndSpan {
@@ -128,7 +128,8 @@ pub fn expand_mod_items(exts: SyntaxExtensions, cx: ext_ctxt,
                   let r = dec_fn(cx, attr.span, attr.node.value, items);
                   cx.bt_pop();
                   r
-              }
+              },
+              _ => items,
             }
         }
     };
@@ -137,34 +138,94 @@ pub fn expand_mod_items(exts: SyntaxExtensions, cx: ext_ctxt,
 }
 
 
+// eval $e with a new exts frame:
+macro_rules! with_exts_frame (
+    ($extsboxexpr:expr,$e:expr) =>
+    ({let extsbox = $extsboxexpr;
+      let oldexts = *extsbox;
+      *extsbox = oldexts.push_frame();
+      let result = $e;
+      *extsbox = oldexts;
+      result
+     })
+)
+
 // When we enter a module, record it, for the sake of `module!`
-pub fn expand_item(exts: SyntaxExtensions,
+pub fn expand_item(extsbox: @mut SyntaxEnv,
                    cx: ext_ctxt, it: @ast::item, fld: ast_fold,
                    orig: fn@(@ast::item, ast_fold) -> Option<@ast::item>)
                 -> Option<@ast::item> {
-    let is_mod = match it.node {
-      ast::item_mod(_) | ast::item_foreign_mod(_) => true,
-      _ => false
-    };
+    // need to do expansion first... it might turn out to be a module.
     let maybe_it = match it.node {
-      ast::item_mac(*) => expand_item_mac(exts, cx, it, fld),
+      ast::item_mac(*) => expand_item_mac(extsbox, cx, it, fld),
       _ => Some(it)
     };
-
     match maybe_it {
       Some(it) => {
-        if is_mod { cx.mod_push(it.ident); }
-        let ret_val = orig(it, fld);
-        if is_mod { cx.mod_pop(); }
-        return ret_val;
+          match it.node {
+              ast::item_mod(_) | ast::item_foreign_mod(_) => {
+                  cx.mod_push(it.ident);
+                  let result =
+                      // don't push a macro scope for macro_escape:
+                      if contains_macro_escape(it.attrs) {
+                      orig(it,fld)
+                  } else {
+                      // otherwise, push a scope:
+                      with_exts_frame!(extsbox,orig(it,fld))
+                  };
+                  cx.mod_pop();
+                  result
+              }
+              _ => orig(it,fld)
+          }
       }
-      None => return None
+      None => None
     }
 }
 
+// does this attribute list contain "macro_escape" ?
+fn contains_macro_escape (attrs: &[ast::attribute]) -> bool{
+    let mut accum = false;
+    do attrs.each |attr| {
+        let mname = attr::get_attr_name(attr);
+        if (mname == @~"macro_escape") {
+            accum = true;
+            false
+        } else {
+            true
+        }
+    }
+    accum
+}
+
+// this macro disables (one layer of) macro
+// scoping, to allow a block to add macro bindings
+// to its parent env
+macro_rules! without_macro_scoping(
+    ($extsexpr:expr,$exp:expr) =>
+    ({
+        // only evaluate this once:
+        let exts = $extsexpr;
+        // capture the existing binding:
+        let existingBlockBinding =
+            match exts.find(&@~" block"){
+                Some(binding) => binding,
+                None => cx.bug("expected to find \" block\" binding")
+            };
+        // this prevents the block from limiting the macros' scope:
+        exts.insert(@~" block",@ScopeMacros(false));
+        let result = $exp;
+        // reset the block binding. Note that since the original
+        // one may have been inherited, this procedure may wind
+        // up introducing a block binding where one didn't exist
+        // before.
+        exts.insert(@~" block",existingBlockBinding);
+        result
+    }))
+
 // Support for item-position macro invocations, exactly the same
 // logic as for expression-position macro invocations.
-pub fn expand_item_mac(exts: SyntaxExtensions,
+pub fn expand_item_mac(+extsbox: @mut SyntaxEnv,
                        cx: ext_ctxt, &&it: @ast::item,
                        fld: ast_fold) -> Option<@ast::item> {
 
@@ -176,11 +237,11 @@ pub fn expand_item_mac(exts: SyntaxExtensions,
     };
 
     let extname = cx.parse_sess().interner.get(pth.idents[0]);
-    let expanded = match exts.find(&extname) {
+    let expanded = match (*extsbox).find(&extname) {
         None => cx.span_fatal(pth.span,
                               fmt!("macro undefined: '%s!'", *extname)),
 
-        Some(NormalTT(ref expand)) => {
+        Some(@SE(NormalTT(ref expand))) => {
             if it.ident != parse::token::special_idents::invalid {
                 cx.span_fatal(pth.span,
                               fmt!("macro %s! expects no ident argument, \
@@ -196,7 +257,7 @@ pub fn expand_item_mac(exts: SyntaxExtensions,
             }));
             ((*expand).expander)(cx, it.span, tts)
         }
-        Some(ItemTT(ref expand)) => {
+        Some(@SE(IdentTT(ref expand))) => {
             if it.ident == parse::token::special_idents::invalid {
                 cx.span_fatal(pth.span,
                               fmt!("macro %s! expects an ident argument",
@@ -223,7 +284,7 @@ pub fn expand_item_mac(exts: SyntaxExtensions,
         MRAny(_, item_maker, _) =>
             option::chain(item_maker(), |i| {fld.fold_item(i)}),
         MRDef(ref mdef) => {
-            exts.insert(@/*bad*/ copy mdef.name, (*mdef).ext);
+            extsbox.insert(@/*bad*/ copy mdef.name, @SE((*mdef).ext));
             None
         }
     };
@@ -231,7 +292,8 @@ pub fn expand_item_mac(exts: SyntaxExtensions,
     return maybe_it;
 }
 
-pub fn expand_stmt(exts: SyntaxExtensions, cx: ext_ctxt,
+// expand a stmt
+pub fn expand_stmt(extsbox: @mut SyntaxEnv, cx: ext_ctxt,
                    s: &stmt_, sp: span, fld: ast_fold,
                    orig: fn@(s: &stmt_, span, ast_fold) -> (stmt_, span))
                 -> (stmt_, span) {
@@ -249,12 +311,12 @@ pub fn expand_stmt(exts: SyntaxExtensions, cx: ext_ctxt,
 
     assert(vec::len(pth.idents) == 1u);
     let extname = cx.parse_sess().interner.get(pth.idents[0]);
-    let (fully_expanded, sp) = match exts.find(&extname) {
+    let (fully_expanded, sp) = match (*extsbox).find(&extname) {
         None =>
             cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", *extname)),
 
-        Some(NormalTT(
-            SyntaxExpanderTT{expander: exp, span: exp_sp})) => {
+        Some(@SE(NormalTT(
+            SyntaxExpanderTT{expander: exp, span: exp_sp}))) => {
             cx.bt_push(ExpandedFrom(CallInfo {
                 call_site: sp,
                 callee: NameAndSpan { name: copy *extname, span: exp_sp }
@@ -282,7 +344,7 @@ pub fn expand_stmt(exts: SyntaxExtensions, cx: ext_ctxt,
         }
     };
 
-    return (match fully_expanded {
+    (match fully_expanded {
         stmt_expr(e, stmt_id) if semi => stmt_semi(e, stmt_id),
         _ => { fully_expanded } /* might already have a semi */
     }, sp)
@@ -290,19 +352,39 @@ pub fn expand_stmt(exts: SyntaxExtensions, cx: ext_ctxt,
 }
 
 
+
+pub fn expand_block(extsbox: @mut SyntaxEnv, cx: ext_ctxt,
+                    blk: &blk_, sp: span, fld: ast_fold,
+                    orig: fn@(&blk_, span, ast_fold) -> (blk_, span))
+    -> (blk_, span) {
+    match (*extsbox).find(&@~" block") {
+        // no scope limit on macros in this block, no need
+        // to push an exts frame:
+        Some(@ScopeMacros(false)) => {
+            orig (blk,sp,fld)
+        },
+        // this block should limit the scope of its macros:
+        Some(@ScopeMacros(true)) => {
+            // see note below about treatment of exts table
+            with_exts_frame!(extsbox,orig(blk,sp,fld))
+        },
+        _ => cx.span_bug(sp,
+                         ~"expected ScopeMacros binding for \" block\"")
+    }
+}
+
 pub fn new_span(cx: ext_ctxt, sp: span) -> span {
     /* this discards information in the case of macro-defining macros */
     return span {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
 }
 
-// FIXME (#2247): this is a terrible kludge to inject some macros into
-// the default compilation environment. When the macro-definition system
-// is substantially more mature, these should move from here, into a
-// compiled part of libcore at very least.
+// FIXME (#2247): this is a moderately bad kludge to inject some macros into
+// the default compilation environment. It would be much nicer to use
+// a mechanism like syntax_quote to ensure hygiene.
 
 pub fn core_macros() -> ~str {
     return
-~"{
+~"pub mod macros {
     macro_rules! ignore (($($x:tt)*) => (()))
 
     macro_rules! error ( ($( $arg:expr ),+) => (
@@ -352,28 +434,166 @@ pub fn core_macros() -> ~str {
 
 pub fn expand_crate(parse_sess: @mut parse::ParseSess,
                     cfg: ast::crate_cfg, c: @crate) -> @crate {
-    let exts = syntax_expander_table();
+    // adding *another* layer of indirection here so that the block
+    // visitor can swap out one exts table for another for the duration
+    // of the block.  The cleaner alternative would be to thread the
+    // exts table through the fold, but that would require updating
+    // every method/element of AstFoldFns in fold.rs.
+    let extsbox = @mut syntax_expander_table();
     let afp = default_ast_fold();
     let cx: ext_ctxt = mk_ctxt(parse_sess, copy cfg);
     let f_pre = @AstFoldFns {
-        fold_expr: |a,b,c| expand_expr(exts, cx, a, b, c, afp.fold_expr),
-        fold_mod: |a,b| expand_mod_items(exts, cx, a, b, afp.fold_mod),
-        fold_item: |a,b| expand_item(exts, cx, a, b, afp.fold_item),
-        fold_stmt: |a,b,c| expand_stmt(exts, cx, a, b, c, afp.fold_stmt),
+        fold_expr: |expr,span,recur|
+            expand_expr(extsbox, cx, expr, span, recur, afp.fold_expr),
+        fold_mod: |modd,recur|
+            expand_mod_items(extsbox, cx, modd, recur, afp.fold_mod),
+        fold_item: |item,recur|
+            expand_item(extsbox, cx, item, recur, afp.fold_item),
+        fold_stmt: |stmt,span,recur|
+            expand_stmt(extsbox, cx, stmt, span, recur, afp.fold_stmt),
+        fold_block: |blk,span,recur|
+            expand_block(extsbox, cx, blk, span, recur, afp.fold_block),
         new_span: |a| new_span(cx, a),
         .. *afp};
     let f = make_fold(f_pre);
-    let cm = parse_expr_from_source_str(~"<core-macros>",
-                                        @core_macros(),
-                                        copy cfg,
-                                        parse_sess);
-
+    // add a bunch of macros as though they were placed at the
+    // head of the program (ick).
+    let attrs = ~[
+        spanned {
+            span: codemap::dummy_sp(),
+            node: attribute_ {
+                style: attr_outer,
+                value: @spanned {
+                    node: meta_word(@~"macro_escape"),
+                    span: codemap::dummy_sp(),
+                },
+                is_sugared_doc: false,
+            }
+        }
+    ];
+
+    let cm = match parse_item_from_source_str(~"<core-macros>",
+                                              @core_macros(),
+                                              copy cfg,
+                                              attrs,
+                                              parse_sess) {
+        Some(item) => item,
+        None => cx.bug(~"expected core macros to parse correctly")
+    };
     // This is run for its side-effects on the expander env,
     // as it registers all the core macros as expanders.
-    f.fold_expr(cm);
+    f.fold_item(cm);
 
     @f.fold_crate(&*c)
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use util::testing::check_equal;
+
+    // make sure that fail! is present
+    #[test] fn fail_exists_test () {
+        let src = ~"fn main() { fail!(~\"something appropriately gloomy\");}";
+        let sess = parse::new_parse_sess(None);
+        let cfg = ~[];
+        let crate_ast = parse::parse_crate_from_source_str(
+            ~"<test>",
+            @src,
+            cfg,sess);
+        expand_crate(sess,cfg,crate_ast);
+    }
+
+    // these following tests are quite fragile, in that they don't test what
+    // *kind* of failure occurs.
+
+    // make sure that macros can leave scope
+    #[should_fail]
+    #[test] fn macros_cant_escape_fns_test () {
+        let src = ~"fn bogus() {macro_rules! z (() => (3+4))}\
+                    fn inty() -> int { z!() }";
+        let sess = parse::new_parse_sess(None);
+        let cfg = ~[];
+        let crate_ast = parse::parse_crate_from_source_str(
+            ~"<test>",
+            @src,
+            cfg,sess);
+        // should fail:
+        expand_crate(sess,cfg,crate_ast);
+    }
+
+    // make sure that macros can leave scope for modules
+    #[should_fail]
+    #[test] fn macros_cant_escape_mods_test () {
+        let src = ~"mod foo {macro_rules! z (() => (3+4))}\
+                    fn inty() -> int { z!() }";
+        let sess = parse::new_parse_sess(None);
+        let cfg = ~[];
+        let crate_ast = parse::parse_crate_from_source_str(
+            ~"<test>",
+            @src,
+            cfg,sess);
+        // should fail:
+        expand_crate(sess,cfg,crate_ast);
+    }
+
+    // macro_escape modules shouldn't cause macros to leave scope
+    #[test] fn macros_can_escape_flattened_mods_test () {
+        let src = ~"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
+                    fn inty() -> int { z!() }";
+        let sess = parse::new_parse_sess(None);
+        let cfg = ~[];
+        let crate_ast = parse::parse_crate_from_source_str(
+            ~"<test>",
+            @src,
+            cfg,sess);
+        // should fail:
+        expand_crate(sess,cfg,crate_ast);
+    }
+
+    #[test] fn core_macros_must_parse () {
+        let src = ~"
+  pub mod macros {
+    macro_rules! ignore (($($x:tt)*) => (()))
+
+    macro_rules! error ( ($( $arg:expr ),+) => (
+        log(::core::error, fmt!( $($arg),+ )) ))
+}";
+        let sess = parse::new_parse_sess(None);
+        let cfg = ~[];
+        let item_ast = parse::parse_item_from_source_str(
+            ~"<test>",
+            @src,
+            cfg,~[make_dummy_attr (@~"macro_escape")],sess);
+        match item_ast {
+            Some(_) => (), // success
+            None => fail!(~"expected this to parse")
+        }
+    }
+
+    #[test] fn test_contains_flatten (){
+        let attr1 = make_dummy_attr (@~"foo");
+        let attr2 = make_dummy_attr (@~"bar");
+        let escape_attr = make_dummy_attr (@~"macro_escape");
+        let attrs1 = ~[attr1, escape_attr, attr2];
+        check_equal (contains_macro_escape (attrs1),true);
+        let attrs2 = ~[attr1,attr2];
+        check_equal (contains_macro_escape (attrs2),false);
+    }
+
+    // make a "meta_word" outer attribute with the given name
+    fn make_dummy_attr(s: @~str) -> ast::attribute {
+        spanned {span:codemap::dummy_sp(),
+                 node: attribute_
+                     {style:attr_outer,
+                      value:spanned
+                          {node:meta_word(s),
+                           span:codemap::dummy_sp()},
+                      is_sugared_doc:false}}
+    }
+
+}
+
 // Local Variables:
 // mode: rust
 // fill-column: 78;
diff --git a/src/libsyntax/ext/pipes/ast_builder.rs b/src/libsyntax/ext/pipes/ast_builder.rs
index 7917a072414..88b77ec7970 100644
--- a/src/libsyntax/ext/pipes/ast_builder.rs
+++ b/src/libsyntax/ext/pipes/ast_builder.rs
@@ -54,7 +54,7 @@ pub trait append_types {
     fn add_tys(&self, +tys: ~[@ast::Ty]) -> @ast::path;
 }
 
-pub impl append_types for @ast::path {
+impl append_types for @ast::path {
     fn add_ty(&self, ty: @ast::Ty) -> @ast::path {
         @ast::path {
             types: vec::append_one(copy self.types, ty),
@@ -127,7 +127,7 @@ pub trait ext_ctxt_ast_builder {
     fn strip_bounds(&self, bounds: &[ast::ty_param]) -> ~[ast::ty_param];
 }
 
-pub impl ext_ctxt_ast_builder for ext_ctxt {
+impl ext_ctxt_ast_builder for ext_ctxt {
     fn ty_option(&self, ty: @ast::Ty) -> @ast::Ty {
         self.ty_path_ast_builder(path_global(~[
             self.ident_of(~"core"),
diff --git a/src/libsyntax/ext/pipes/check.rs b/src/libsyntax/ext/pipes/check.rs
index cc42a0992cb..f456f7b81ae 100644
--- a/src/libsyntax/ext/pipes/check.rs
+++ b/src/libsyntax/ext/pipes/check.rs
@@ -37,7 +37,7 @@ use ext::base::ext_ctxt;
 use ext::pipes::proto::{state, protocol, next_state};
 use ext::pipes::proto;
 
-pub impl proto::visitor<(), (), ()> for ext_ctxt {
+impl proto::visitor<(), (), ()> for ext_ctxt {
     fn visit_proto(&self, _proto: protocol,
                    _states: &[()]) { }
 
diff --git a/src/libsyntax/ext/pipes/parse_proto.rs b/src/libsyntax/ext/pipes/parse_proto.rs
index ce253f6156b..12603200ef3 100644
--- a/src/libsyntax/ext/pipes/parse_proto.rs
+++ b/src/libsyntax/ext/pipes/parse_proto.rs
@@ -23,7 +23,7 @@ pub trait proto_parser {
     fn parse_message(&self, state: state);
 }
 
-pub impl proto_parser for parser::Parser {
+impl proto_parser for parser::Parser {
     fn parse_proto(&self, +id: ~str) -> protocol {
         let proto = protocol(id, *self.span);
 
diff --git a/src/libsyntax/ext/pipes/pipec.rs b/src/libsyntax/ext/pipes/pipec.rs
index be25876cfbf..46804c3d075 100644
--- a/src/libsyntax/ext/pipes/pipec.rs
+++ b/src/libsyntax/ext/pipes/pipec.rs
@@ -46,7 +46,7 @@ pub trait gen_init {
     fn gen_init_bounded(&self, ext_cx: ext_ctxt) -> @ast::expr;
 }
 
-pub impl gen_send for message {
+impl gen_send for message {
     fn gen_send(&mut self, cx: ext_ctxt, try: bool) -> @ast::item {
         debug!("pipec: gen_send");
         let name = self.name();
@@ -195,7 +195,7 @@ pub impl gen_send for message {
     }
 }
 
-pub impl to_type_decls for state {
+impl to_type_decls for state {
     fn to_type_decls(&self, cx: ext_ctxt) -> ~[@ast::item] {
         debug!("pipec: to_type_decls");
         // This compiles into two different type declarations. Say the
@@ -306,7 +306,7 @@ pub impl to_type_decls for state {
     }
 }
 
-pub impl gen_init for protocol {
+impl gen_init for protocol {
     fn gen_init(&self, cx: ext_ctxt) -> @ast::item {
         let ext_cx = cx;
 
diff --git a/src/libsyntax/ext/pipes/proto.rs b/src/libsyntax/ext/pipes/proto.rs
index 52eb88d0700..48abd6f1d97 100644
--- a/src/libsyntax/ext/pipes/proto.rs
+++ b/src/libsyntax/ext/pipes/proto.rs
@@ -21,7 +21,7 @@ use core::to_str::ToStr;
 #[deriving_eq]
 pub enum direction { send, recv }
 
-pub impl ToStr for direction {
+impl ToStr for direction {
     pure fn to_str(&self) -> ~str {
         match *self {
           send => ~"Send",
diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs
index c4049b796c5..a8a7ad4c749 100644
--- a/src/libsyntax/ext/source_util.rs
+++ b/src/libsyntax/ext/source_util.rs
@@ -22,36 +22,9 @@ use core::result;
 use core::str;
 use core::vec;
 
-fn topmost_expn_info(expn_info: @codemap::ExpnInfo) -> @codemap::ExpnInfo {
-    // FIXME(#3874): this would be better written as:
-    // let @ExpandedFrom(CallInfo {
-    //     call_site: ref call_site,
-    //     _
-    //  }) = expn_info;
-    match *expn_info {
-        ExpandedFrom(CallInfo { call_site: ref call_site, _}) => {
-            match call_site.expn_info {
-                Some(next_expn_info) => {
-                    // Don't recurse into file using "include!"
-                    match *next_expn_info {
-                        ExpandedFrom(
-                            CallInfo { callee: NameAndSpan {
-                                name: ref name,
-                                _
-                            },
-                            _
-                        }) => {
-                            if *name == ~"include" { return expn_info; }
-                        }
-                    }
-
-                    topmost_expn_info(next_expn_info)
-                },
-                None => expn_info
-            }
-        }
-    }
-}
+// These macros all relate to the file system; they either return
+// the column/row/filename of the expression, or they include
+// a given file into the current one.
 
 /* line!(): expands to the current line number */
 pub fn expand_line(cx: ext_ctxt, sp: span, tts: &[ast::token_tree])
@@ -101,6 +74,9 @@ pub fn expand_mod(cx: ext_ctxt, sp: span, tts: &[ast::token_tree])
                                   |x| cx.str_of(*x)), ~"::")))
 }
 
+// include! : parse the given file as an expr
+// This is generally a bad idea because it's going to behave
+// unhygienically.
 pub fn expand_include(cx: ext_ctxt, sp: span, tts: &[ast::token_tree])
     -> base::MacResult {
     let file = get_single_str_from_tts(cx, sp, tts, "include!");
@@ -110,6 +86,7 @@ pub fn expand_include(cx: ext_ctxt, sp: span, tts: &[ast::token_tree])
     base::MRExpr(p.parse_expr())
 }
 
+// include_str! : read the given file, insert it as a literal string expr
 pub fn expand_include_str(cx: ext_ctxt, sp: span, tts: &[ast::token_tree])
     -> base::MacResult {
     let file = get_single_str_from_tts(cx, sp, tts, "include_str!");
@@ -140,6 +117,26 @@ pub fn expand_include_bin(cx: ext_ctxt, sp: span, tts: &[ast::token_tree])
     }
 }
 
+// recur along an ExpnInfo chain to find the original expression
+fn topmost_expn_info(expn_info: @codemap::ExpnInfo) -> @codemap::ExpnInfo {
+    let ExpandedFrom(CallInfo { call_site, _ }) = *expn_info;
+    match call_site.expn_info {
+        Some(next_expn_info) => {
+            let ExpandedFrom(CallInfo {
+                callee: NameAndSpan {name, _},
+                _
+            }) = *next_expn_info;
+            // Don't recurse into file using "include!"
+            if name == ~"include" { return expn_info; }
+
+            topmost_expn_info(next_expn_info)
+        },
+        None => expn_info
+    }
+}
+
+// resolve a file-system path to an absolute file-system path (if it
+// isn't already)
 fn res_rel_file(cx: ext_ctxt, sp: codemap::span, arg: &Path) -> Path {
     // NB: relative paths are resolved relative to the compilation unit
     if !arg.is_absolute {
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 68d05e11171..c23ce1c14a2 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -780,7 +780,7 @@ pub fn default_ast_fold() -> ast_fold_fns {
     }
 }
 
-pub impl ast_fold for ast_fold_fns {
+impl ast_fold for ast_fold_fns {
     /* naturally, a macro to write these would be nice */
     fn fold_crate(c: &crate) -> crate {
         let (n, s) = (self.fold_crate)(&c.node, c.span, self as ast_fold);
diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs
index 54fdcc647ea..2a40b700c11 100644
--- a/src/libsyntax/parse/lexer.rs
+++ b/src/libsyntax/parse/lexer.rs
@@ -133,7 +133,7 @@ impl reader for StringReader {
     fn dup(@mut self) -> reader { dup_string_reader(self) as reader }
 }
 
-pub impl reader for TtReader {
+impl reader for TtReader {
     fn is_eof(@mut self) -> bool { self.cur_tok == token::EOF }
     fn next_token(@mut self) -> TokenAndSpan { tt_next_token(self) }
     fn fatal(@mut self, m: ~str) -> ! {
@@ -779,11 +779,13 @@ pub mod test {
     use diagnostic;
     use util::testing::{check_equal, check_equal_ptr};
 
+    // represents a testing reader (incl. both reader and interner)
     struct Env {
         interner: @token::ident_interner,
         string_reader: @mut StringReader
     }
 
+    // open a string reader for the given string
     fn setup(teststr: ~str) -> Env {
         let cm = CodeMap::new();
         let fm = cm.new_filemap(~"zebra.rs", @teststr);
@@ -818,6 +820,52 @@ pub mod test {
         check_equal (string_reader.last_pos,BytePos(29))
     }
 
+    // check that the given reader produces the desired stream
+    // of tokens (stop checking after exhausting the expected vec)
+    fn check_tokenization (env: Env, expected: ~[token::Token]) {
+        for expected.each |expected_tok| {
+            let TokenAndSpan {tok:actual_tok, sp: _} =
+                env.string_reader.next_token();
+            check_equal(&actual_tok,expected_tok);
+        }
+    }
+
+    // make the identifier by looking up the string in the interner
+    fn mk_ident (env: Env, id: ~str, is_mod_name: bool) -> token::Token {
+        token::IDENT (env.interner.intern(@id),is_mod_name)
+    }
+
+    #[test] fn doublecolonparsing () {
+        let env = setup (~"a b");
+        check_tokenization (env,
+                           ~[mk_ident (env,~"a",false),
+                             mk_ident (env,~"b",false)]);
+    }
+
+    #[test] fn dcparsing_2 () {
+        let env = setup (~"a::b");
+        check_tokenization (env,
+                           ~[mk_ident (env,~"a",true),
+                             token::MOD_SEP,
+                             mk_ident (env,~"b",false)]);
+    }
+
+    #[test] fn dcparsing_3 () {
+        let env = setup (~"a ::b");
+        check_tokenization (env,
+                           ~[mk_ident (env,~"a",false),
+                             token::MOD_SEP,
+                             mk_ident (env,~"b",false)]);
+    }
+
+    #[test] fn dcparsing_4 () {
+        let env = setup (~"a:: b");
+        check_tokenization (env,
+                           ~[mk_ident (env,~"a",true),
+                             token::MOD_SEP,
+                             mk_ident (env,~"b",false)]);
+    }
+
     #[test] fn character_a() {
         let env = setup(~"'a'");
         let TokenAndSpan {tok, sp: _} =
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index d8c3ca06d76..816e4137126 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -104,9 +104,7 @@ pub fn parse_crate_from_source_str(
         codemap::FssNone,
         source
     );
-    let r = p.parse_crate_mod(/*bad*/ copy cfg);
-    p.abort_if_errors();
-    r
+    maybe_aborted(p.parse_crate_mod(/*bad*/ copy cfg),p)
 }
 
 pub fn parse_expr_from_source_str(
@@ -122,9 +120,7 @@ pub fn parse_expr_from_source_str(
         codemap::FssNone,
         source
     );
-    let r = p.parse_expr();
-    p.abort_if_errors();
-    r
+    maybe_aborted(p.parse_expr(), p)
 }
 
 pub fn parse_item_from_source_str(
@@ -141,9 +137,7 @@ pub fn parse_item_from_source_str(
         codemap::FssNone,
         source
     );
-    let r = p.parse_item(attrs);
-    p.abort_if_errors();
-    r
+    maybe_aborted(p.parse_item(attrs),p)
 }
 
 pub fn parse_stmt_from_source_str(
@@ -160,9 +154,7 @@ pub fn parse_stmt_from_source_str(
         codemap::FssNone,
         source
     );
-    let r = p.parse_stmt(attrs);
-    p.abort_if_errors();
-    r
+    maybe_aborted(p.parse_stmt(attrs),p)
 }
 
 pub fn parse_tts_from_source_str(
@@ -179,9 +171,7 @@ pub fn parse_tts_from_source_str(
         source
     );
     *p.quote_depth += 1u;
-    let r = p.parse_all_token_trees();
-    p.abort_if_errors();
-    r
+    maybe_aborted(p.parse_all_token_trees(),p)
 }
 
 pub fn parse_from_source_str<T>(
@@ -202,8 +192,7 @@ pub fn parse_from_source_str<T>(
     if !p.reader.is_eof() {
         p.reader.fatal(~"expected end-of-string");
     }
-    p.abort_if_errors();
-    r
+    maybe_aborted(r,p)
 }
 
 pub fn next_node_id(sess: @mut ParseSess) -> node_id {
@@ -230,8 +219,8 @@ pub fn new_parser_from_source_str(
     Parser(sess, cfg, srdr as reader)
 }
 
-// Read the entire source file, return a parser
-// that draws from that string
+/// Read the entire source file, return a parser
+/// that draws from that string
 pub fn new_parser_result_from_file(
     sess: @mut ParseSess,
     +cfg: ast::crate_cfg,
@@ -252,7 +241,7 @@ pub fn new_parser_result_from_file(
     }
 }
 
-/// Create a new parser for an entire crate, handling errors as appropriate
+/// Create a new parser, handling errors as appropriate
 /// if the file doesn't exist
 pub fn new_parser_from_file(
     sess: @mut ParseSess,
@@ -297,6 +286,13 @@ pub fn new_parser_from_tts(
     Parser(sess, cfg, trdr as reader)
 }
 
+// abort if necessary
+pub fn maybe_aborted<T>(+result : T, p: Parser) -> T {
+    p.abort_if_errors();
+    result
+}
+
+
 
 #[cfg(test)]
 mod test {
diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs
index 96ed81e476e..2b2f1f48034 100644
--- a/src/libsyntax/parse/obsolete.rs
+++ b/src/libsyntax/parse/obsolete.rs
@@ -48,9 +48,12 @@ pub enum ObsoleteSyntax {
     ObsoleteUnenforcedBound,
     ObsoleteImplSyntax,
     ObsoleteTraitBoundSeparator,
+    ObsoleteMutOwnedPointer,
+    ObsoleteMutVector,
+    ObsoleteTraitImplVisibility,
 }
 
-pub impl to_bytes::IterBytes for ObsoleteSyntax {
+impl to_bytes::IterBytes for ObsoleteSyntax {
     #[inline(always)]
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         (*self as uint).iter_bytes(lsb0, f);
@@ -126,6 +129,24 @@ pub impl Parser {
                 "space-separated trait bounds",
                 "write `+` between trait bounds"
             ),
+            ObsoleteMutOwnedPointer => (
+                "const or mutable owned pointer",
+                "mutability inherits through `~` pointers; place the `~` box
+                 in a mutable location, like a mutable local variable or an \
+                 `@mut` box"
+            ),
+            ObsoleteMutVector => (
+                "const or mutable vector",
+                "mutability inherits through `~` pointers; place the vector \
+                 in a mutable location, like a mutable local variable or an \
+                 `@mut` box"
+            ),
+            ObsoleteTraitImplVisibility => (
+                "visibility-qualified trait implementation",
+                "`pub` or `priv` is meaningless for trait implementations, \
+                 because the `impl...for...` form defines overloads for \
+                 methods that already exist; remove the `pub` or `priv`"
+            ),
         };
 
         self.report(sp, kind, kind_str, desc);
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index dffa04ac1ca..3d4e0f9020b 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -75,7 +75,8 @@ use parse::obsolete::{ObsoleteMoveInit, ObsoleteBinaryMove};
 use parse::obsolete::{ObsoleteStructCtor, ObsoleteWith};
 use parse::obsolete::{ObsoleteSyntax, ObsoleteLowerCaseKindBounds};
 use parse::obsolete::{ObsoleteUnsafeBlock, ObsoleteImplSyntax};
-use parse::obsolete::{ObsoleteTraitBoundSeparator};
+use parse::obsolete::{ObsoleteTraitBoundSeparator, ObsoleteMutOwnedPointer};
+use parse::obsolete::{ObsoleteMutVector, ObsoleteTraitImplVisibility};
 use parse::prec::{as_prec, token_to_binop};
 use parse::token::{can_begin_expr, is_ident, is_ident_or_path};
 use parse::token::{is_plain_ident, INTERPOLATED, special_idents};
@@ -653,6 +654,9 @@ pub impl Parser {
         } else if *self.token == token::LBRACKET {
             self.expect(&token::LBRACKET);
             let mt = self.parse_mt();
+            if mt.mutbl == m_mutbl {    // `m_const` too after snapshot
+                self.obsolete(*self.last_span, ObsoleteMutVector);
+            }
 
             // Parse the `* 3` in `[ int * 3 ]`
             let t = match self.maybe_parse_fixed_vstore_with_star() {
@@ -710,6 +714,11 @@ pub impl Parser {
         // rather than boxed ptrs.  But the special casing of str/vec is not
         // reflected in the AST type.
         let mt = self.parse_mt();
+
+        if mt.mutbl != m_imm && sigil == OwnedSigil {
+            self.obsolete(*self.last_span, ObsoleteMutOwnedPointer);
+        }
+
         ctor(mt)
     }
 
@@ -781,18 +790,6 @@ pub impl Parser {
         }
     }
 
-    fn parse_capture_item_or(parse_arg_fn: fn(&Parser) -> arg_or_capture_item)
-        -> arg_or_capture_item
-    {
-        if self.eat_keyword(&~"copy") {
-            // XXX outdated syntax now that moves-based-on-type has gone in
-            self.parse_ident();
-            either::Right(())
-        } else {
-            parse_arg_fn(&self)
-        }
-    }
-
     // This version of parse arg doesn't necessarily require
     // identifier names.
     fn parse_arg_general(require_name: bool) -> arg {
@@ -821,32 +818,26 @@ pub impl Parser {
         either::Left(self.parse_arg_general(true))
     }
 
-    fn parse_arg_or_capture_item() -> arg_or_capture_item {
-        self.parse_capture_item_or(|p| p.parse_arg())
-    }
-
     fn parse_fn_block_arg() -> arg_or_capture_item {
-        do self.parse_capture_item_or |p| {
-            let m = p.parse_arg_mode();
-            let is_mutbl = self.eat_keyword(&~"mut");
-            let pat = p.parse_pat(false);
-            let t = if p.eat(&token::COLON) {
-                p.parse_ty(false)
-            } else {
-                @Ty {
-                    id: p.get_id(),
-                    node: ty_infer,
-                    span: mk_sp(p.span.lo, p.span.hi),
-                }
-            };
-            either::Left(ast::arg {
-                mode: m,
-                is_mutbl: is_mutbl,
-                ty: t,
-                pat: pat,
-                id: p.get_id()
-            })
-        }
+        let m = self.parse_arg_mode();
+        let is_mutbl = self.eat_keyword(&~"mut");
+        let pat = self.parse_pat(false);
+        let t = if self.eat(&token::COLON) {
+            self.parse_ty(false)
+        } else {
+            @Ty {
+                id: self.get_id(),
+                node: ty_infer,
+                span: mk_sp(self.span.lo, self.span.hi),
+            }
+        };
+        either::Left(ast::arg {
+            mode: m,
+            is_mutbl: is_mutbl,
+            ty: t,
+            pat: pat,
+            id: self.get_id()
+        })
     }
 
     fn maybe_parse_fixed_vstore_with_star() -> Option<uint> {
@@ -1184,6 +1175,10 @@ pub impl Parser {
         } else if *self.token == token::LBRACKET {
             self.bump();
             let mutbl = self.parse_mutability();
+            if mutbl == m_mutbl {   // `m_const` too after snapshot
+                self.obsolete(*self.last_span, ObsoleteMutVector);
+            }
+
             if *self.token == token::RBRACKET {
                 // Empty vector.
                 self.bump();
@@ -1659,6 +1654,10 @@ pub impl Parser {
           token::TILDE => {
             self.bump();
             let m = self.parse_mutability();
+            if m != m_imm {
+                self.obsolete(*self.last_span, ObsoleteMutOwnedPointer);
+            }
+
             let e = self.parse_prefix_expr();
             hi = e.span.hi;
             // HACK: turn ~[...] into a ~-evec
@@ -1794,7 +1793,7 @@ pub impl Parser {
 
         // if we want to allow fn expression argument types to be inferred in
         // the future, just have to change parse_arg to parse_fn_block_arg.
-        let decl = self.parse_fn_decl(|p| p.parse_arg_or_capture_item());
+        let decl = self.parse_fn_decl(|p| p.parse_arg());
 
         let body = self.parse_block();
 
@@ -3044,9 +3043,9 @@ pub impl Parser {
     }
 
     // Parses two variants (with the region/type params always optional):
-    //    impl<T> ~[T] : to_str { ... }
-    //    impl<T> to_str for ~[T] { ... }
-    fn parse_item_impl() -> item_info {
+    //    impl<T> Foo { ... }
+    //    impl<T> ToStr for ~[T] { ... }
+    fn parse_item_impl(visibility: ast::visibility) -> item_info {
         fn wrap_path(p: &Parser, pt: @path) -> @Ty {
             @Ty {
                 id: p.get_id(),
@@ -3095,6 +3094,12 @@ pub impl Parser {
             None
         };
 
+        // Do not allow visibility to be specified in `impl...for...`. It is
+        // meaningless.
+        if opt_trait.is_some() && visibility != ast::inherited {
+            self.obsolete(*self.span, ObsoleteTraitImplVisibility);
+        }
+
         let mut meths = ~[];
         if !self.eat(&token::SEMI) {
             self.expect(&token::LBRACE);
@@ -3993,7 +3998,8 @@ pub impl Parser {
                                           maybe_append(attrs, extra_attrs)));
         } else if items_allowed && self.eat_keyword(&~"impl") {
             // IMPL ITEM
-            let (ident, item_, extra_attrs) = self.parse_item_impl();
+            let (ident, item_, extra_attrs) =
+                self.parse_item_impl(visibility);
             return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_,
                                           visibility,
                                           maybe_append(attrs, extra_attrs)));
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 8b063314c9b..6d0ca2c6657 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -87,7 +87,9 @@ pub enum Token {
     LIT_STR(ast::ident),
 
     /* Name components */
-    // an identifier contains an "is_mod_name" boolean.
+    // an identifier contains an "is_mod_name" boolean,
+    // indicating whether :: follows this token with no
+    // whitespace in between.
     IDENT(ast::ident, bool),
     UNDERSCORE,
     LIFETIME(ast::ident),