about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ext/asm.rs7
-rw-r--r--src/libsyntax/ext/base.rs9
-rw-r--r--src/libsyntax/ext/concat_idents.rs9
-rw-r--r--src/libsyntax/ext/expand.rs60
-rw-r--r--src/libsyntax/ext/log_syntax.rs8
-rw-r--r--src/libsyntax/ext/trace_macros.rs10
-rw-r--r--src/libsyntax/feature_gate.rs252
-rw-r--r--src/libsyntax/lib.rs4
-rw-r--r--src/libsyntax/print/pprust.rs10
9 files changed, 294 insertions, 75 deletions
diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs
index 1ceda2e08dd..d8cba139fb5 100644
--- a/src/libsyntax/ext/asm.rs
+++ b/src/libsyntax/ext/asm.rs
@@ -18,6 +18,7 @@ use codemap;
 use codemap::Span;
 use ext::base;
 use ext::base::*;
+use feature_gate;
 use parse::token::InternedString;
 use parse::token;
 use ptr::P;
@@ -48,6 +49,12 @@ static OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
 
 pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
                        -> Box<base::MacResult+'cx> {
+    if !cx.ecfg.enable_asm() {
+        feature_gate::emit_feature_err(
+            &cx.parse_sess.span_diagnostic, "asm", sp, feature_gate::EXPLAIN_ASM);
+        return DummyResult::expr(sp);
+    }
+
     let mut p = cx.new_parser_from_tts(tts);
     let mut asm = InternedString::new("");
     let mut asm_str_style = None;
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 083039995ee..8800ffd1e9b 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -439,7 +439,8 @@ impl BlockInfo {
 
 /// The base map of methods for expanding syntax extension
 /// AST nodes into full ASTs
-fn initial_syntax_expander_table(ecfg: &expand::ExpansionConfig) -> SyntaxEnv {
+fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
+                                        -> SyntaxEnv {
     // utility function to simplify creating NormalTT syntax extensions
     fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension {
         NormalTT(box f, None)
@@ -470,7 +471,7 @@ fn initial_syntax_expander_table(ecfg: &expand::ExpansionConfig) -> SyntaxEnv {
     syntax_expanders.insert(intern("deriving"),
                             Decorator(box ext::deriving::expand_deprecated_deriving));
 
-    if ecfg.enable_quotes {
+    if ecfg.enable_quotes() {
         // Quasi-quoting expanders
         syntax_expanders.insert(intern("quote_tokens"),
                            builtin_normal_expander(
@@ -541,7 +542,7 @@ pub struct ExtCtxt<'a> {
     pub parse_sess: &'a parse::ParseSess,
     pub cfg: ast::CrateConfig,
     pub backtrace: ExpnId,
-    pub ecfg: expand::ExpansionConfig,
+    pub ecfg: expand::ExpansionConfig<'a>,
     pub use_std: bool,
 
     pub mod_path: Vec<ast::Ident> ,
@@ -554,7 +555,7 @@ pub struct ExtCtxt<'a> {
 
 impl<'a> ExtCtxt<'a> {
     pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
-               ecfg: expand::ExpansionConfig) -> ExtCtxt<'a> {
+               ecfg: expand::ExpansionConfig<'a>) -> ExtCtxt<'a> {
         let env = initial_syntax_expander_table(&ecfg);
         ExtCtxt {
             parse_sess: parse_sess,
diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs
index 364cacd735c..63a8bd9ddf1 100644
--- a/src/libsyntax/ext/concat_idents.rs
+++ b/src/libsyntax/ext/concat_idents.rs
@@ -12,12 +12,21 @@ use ast;
 use codemap::Span;
 use ext::base::*;
 use ext::base;
+use feature_gate;
 use parse::token;
 use parse::token::{str_to_ident};
 use ptr::P;
 
 pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
                               -> Box<base::MacResult+'cx> {
+    if !cx.ecfg.enable_concat_idents() {
+        feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
+                                       "concat_idents",
+                                       sp,
+                                       feature_gate::EXPLAIN_CONCAT_IDENTS);
+        return base::DummyResult::expr(sp);
+    }
+
     let mut res_str = String::new();
     for (i, e) in tts.iter().enumerate() {
         if i & 1 == 1 {
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index fd98f42c2ab..6b7cecee815 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -22,6 +22,7 @@ use attr::AttrMetaMethods;
 use codemap;
 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
 use ext::base::*;
+use feature_gate::{Features};
 use fold;
 use fold::*;
 use parse;
@@ -1408,28 +1409,63 @@ fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
     }
 }
 
-pub struct ExpansionConfig {
+pub struct ExpansionConfig<'feat> {
     pub crate_name: String,
-    pub enable_quotes: bool,
+    pub features: Option<&'feat Features>,
     pub recursion_limit: usize,
 }
 
-impl ExpansionConfig {
-    pub fn default(crate_name: String) -> ExpansionConfig {
+impl<'feat> ExpansionConfig<'feat> {
+    pub fn default(crate_name: String) -> ExpansionConfig<'static> {
         ExpansionConfig {
             crate_name: crate_name,
-            enable_quotes: false,
+            features: None,
             recursion_limit: 64,
         }
     }
+
+    pub fn enable_quotes(&self) -> bool {
+        match self.features {
+            Some(&Features { allow_quote: true, .. }) => true,
+            _ => false,
+        }
+    }
+
+    pub fn enable_asm(&self) -> bool {
+        match self.features {
+            Some(&Features { allow_asm: true, .. }) => true,
+            _ => false,
+        }
+    }
+
+    pub fn enable_log_syntax(&self) -> bool {
+        match self.features {
+            Some(&Features { allow_log_syntax: true, .. }) => true,
+            _ => false,
+        }
+    }
+
+    pub fn enable_concat_idents(&self) -> bool {
+        match self.features {
+            Some(&Features { allow_concat_idents: true, .. }) => true,
+            _ => false,
+        }
+    }
+
+    pub fn enable_trace_macros(&self) -> bool {
+        match self.features {
+            Some(&Features { allow_trace_macros: true, .. }) => true,
+            _ => false,
+        }
+    }
 }
 
-pub fn expand_crate(parse_sess: &parse::ParseSess,
-                    cfg: ExpansionConfig,
-                    // these are the macros being imported to this crate:
-                    imported_macros: Vec<ast::MacroDef>,
-                    user_exts: Vec<NamedSyntaxExtension>,
-                    c: Crate) -> Crate {
+pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
+                           cfg: ExpansionConfig<'feat>,
+                           // these are the macros being imported to this crate:
+                           imported_macros: Vec<ast::MacroDef>,
+                           user_exts: Vec<NamedSyntaxExtension>,
+                           c: Crate) -> Crate {
     let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
     cx.use_std = std_inject::use_std(&c);
 
@@ -1598,7 +1634,7 @@ mod test {
     // these following tests are quite fragile, in that they don't test what
     // *kind* of failure occurs.
 
-    fn test_ecfg() -> ExpansionConfig {
+    fn test_ecfg() -> ExpansionConfig<'static> {
         ExpansionConfig::default("test".to_string())
     }
 
diff --git a/src/libsyntax/ext/log_syntax.rs b/src/libsyntax/ext/log_syntax.rs
index 30301e3b8cc..8173dd93f74 100644
--- a/src/libsyntax/ext/log_syntax.rs
+++ b/src/libsyntax/ext/log_syntax.rs
@@ -11,12 +11,20 @@
 use ast;
 use codemap;
 use ext::base;
+use feature_gate;
 use print;
 
 pub fn expand_syntax_ext<'cx>(cx: &'cx mut base::ExtCtxt,
                               sp: codemap::Span,
                               tts: &[ast::TokenTree])
                               -> Box<base::MacResult+'cx> {
+    if !cx.ecfg.enable_log_syntax() {
+        feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
+                                       "log_syntax",
+                                       sp,
+                                       feature_gate::EXPLAIN_LOG_SYNTAX);
+        return base::DummyResult::any(sp);
+    }
 
     cx.print_backtrace();
 
diff --git a/src/libsyntax/ext/trace_macros.rs b/src/libsyntax/ext/trace_macros.rs
index 76f7b7b0d7b..3fcc6a8d692 100644
--- a/src/libsyntax/ext/trace_macros.rs
+++ b/src/libsyntax/ext/trace_macros.rs
@@ -12,6 +12,7 @@ use ast;
 use codemap::Span;
 use ext::base::ExtCtxt;
 use ext::base;
+use feature_gate;
 use parse::token::keywords;
 
 
@@ -19,6 +20,15 @@ pub fn expand_trace_macros(cx: &mut ExtCtxt,
                            sp: Span,
                            tt: &[ast::TokenTree])
                            -> Box<base::MacResult+'static> {
+    if !cx.ecfg.enable_trace_macros() {
+        feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
+                                       "trace_macros",
+                                       sp,
+                                       feature_gate::EXPLAIN_TRACE_MACROS);
+        return base::DummyResult::any(sp);
+    }
+
+
     match tt {
         [ast::TtToken(_, ref tok)] if tok.is_keyword(keywords::True) => {
             cx.set_trace_macros(true);
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index fd1ca11818c..3bebba15a57 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -23,6 +23,7 @@
 //! becomes stable.
 
 use self::Status::*;
+use self::AttributeType::*;
 
 use abi::RustIntrinsic;
 use ast::NodeId;
@@ -35,7 +36,6 @@ use visit;
 use visit::Visitor;
 use parse::token::{self, InternedString};
 
-use std::slice;
 use std::ascii::AsciiExt;
 
 // If you change this list without updating src/doc/reference.md, @cmr will be sad
@@ -133,6 +133,12 @@ static KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
     // Allows using the unsafe_no_drop_flag attribute (unlikely to
     // switch to Accepted; see RFC 320)
     ("unsafe_no_drop_flag", "1.0.0", Active),
+
+    // Allows the use of custom attributes; RFC 572
+    ("custom_attribute", "1.0.0", Active),
+
+    // Allows the use of rustc_* attributes; RFC 572
+    ("rustc_attrs", "1.0.0", Active),
 ];
 // (changing above list without updating src/doc/reference.md makes @cmr sad)
 
@@ -152,12 +158,148 @@ enum Status {
     Accepted,
 }
 
+// Attributes that have a special meaning to rustc or rustdoc
+pub static KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
+    // Normal attributes
+
+    ("warn", Normal),
+    ("allow", Normal),
+    ("forbid", Normal),
+    ("deny", Normal),
+
+    ("macro_reexport", Normal),
+    ("macro_use", Normal),
+    ("macro_export", Normal),
+    ("plugin_registrar", Normal),
+
+    ("cfg", Normal),
+    ("main", Normal),
+    ("start", Normal),
+    ("test", Normal),
+    ("bench", Normal),
+    ("simd", Normal),
+    ("repr", Normal),
+    ("path", Normal),
+    ("abi", Normal),
+    ("unsafe_destructor", Normal),
+    ("automatically_derived", Normal),
+    ("no_mangle", Normal),
+    ("no_link", Normal),
+    ("derive", Normal),
+    ("should_fail", Normal),
+    ("ignore", Normal),
+    ("no_implicit_prelude", Normal),
+    ("reexport_test_harness_main", Normal),
+    ("link_args", Normal),
+    ("macro_escape", Normal),
+
+
+    ("staged_api", Gated("staged_api",
+                         "staged_api is for use by rustc only")),
+    ("plugin", Gated("plugin",
+                     "compiler plugins are experimental \
+                      and possibly buggy")),
+    ("no_std", Gated("no_std",
+                     "no_std is experimental")),
+    ("lang", Gated("lang_items",
+                     "language items are subject to change")),
+    ("linkage", Gated("linkage",
+                      "the `linkage` attribute is experimental \
+                       and not portable across platforms")),
+    ("thread_local", Gated("thread_local",
+                            "`#[thread_local]` is an experimental feature, and does not \
+                             currently handle destructors. There is no corresponding \
+                             `#[task_local]` mapping to the task model")),
+
+    ("rustc_on_unimplemented", Gated("on_unimplemented",
+                                     "the `#[rustc_on_unimplemented]` attribute \
+                                      is an experimental feature")),
+    ("rustc_variance", Gated("rustc_attrs",
+                             "the `#[rustc_variance]` attribute \
+                              is an experimental feature")),
+    ("rustc_error", Gated("rustc_attrs",
+                          "the `#[rustc_error]` attribute \
+                           is an experimental feature")),
+    ("rustc_move_fragments", Gated("rustc_attrs",
+                                   "the `#[rustc_move_fragments]` attribute \
+                                    is an experimental feature")),
+
+    // FIXME: #14408 whitelist docs since rustdoc looks at them
+    ("doc", Whitelisted),
+
+    // FIXME: #14406 these are processed in trans, which happens after the
+    // lint pass
+    ("cold", Whitelisted),
+    ("export_name", Whitelisted),
+    ("inline", Whitelisted),
+    ("link", Whitelisted),
+    ("link_name", Whitelisted),
+    ("link_section", Whitelisted),
+    ("no_builtins", Whitelisted),
+    ("no_mangle", Whitelisted),
+    ("no_split_stack", Whitelisted),
+    ("no_stack_check", Whitelisted),
+    ("packed", Whitelisted),
+    ("static_assert", Whitelisted),
+    ("no_debug", Whitelisted),
+    ("omit_gdb_pretty_printer_section", Whitelisted),
+    ("unsafe_no_drop_flag", Whitelisted),
+
+    // used in resolve
+    ("prelude_import", Whitelisted),
+
+    // FIXME: #14407 these are only looked at on-demand so we can't
+    // guarantee they'll have already been checked
+    ("deprecated", Whitelisted),
+    ("must_use", Whitelisted),
+    ("stable", Whitelisted),
+    ("unstable", Whitelisted),
+
+    // FIXME: #19470 this shouldn't be needed forever
+    ("old_orphan_check", Whitelisted),
+    ("old_impl_check", Whitelisted),
+    ("rustc_paren_sugar", Whitelisted), // FIXME: #18101 temporary unboxed closure hack
+
+    // Crate level attributes
+    ("crate_name", CrateLevel),
+    ("crate_type", CrateLevel),
+    ("crate_id", CrateLevel),
+    ("feature", CrateLevel),
+    ("no_start", CrateLevel),
+    ("no_main", CrateLevel),
+    ("no_builtins", CrateLevel),
+    ("recursion_limit", CrateLevel),
+];
+
+#[derive(PartialEq, Copy)]
+pub enum AttributeType {
+    /// Normal, builtin attribute that is consumed
+    /// by the compiler before the unused_attribute check
+    Normal,
+
+    /// Builtin attribute that may not be consumed by the compiler
+    /// before the unused_attribute check. These attributes
+    /// will be ignored by the unused_attribute lint
+    Whitelisted,
+
+    /// Is gated by a given feature gate and reason
+    /// These get whitelisted too
+    Gated(&'static str, &'static str),
+
+    /// Builtin attribute that is only allowed at the crate level
+    CrateLevel,
+}
+
 /// A set of features to be used by later passes.
 pub struct Features {
     pub unboxed_closures: bool,
     pub rustc_diagnostic_macros: bool,
     pub visible_private_types: bool,
-    pub quote: bool,
+    pub allow_quote: bool,
+    pub allow_asm: bool,
+    pub allow_log_syntax: bool,
+    pub allow_concat_idents: bool,
+    pub allow_trace_macros: bool,
     pub old_orphan_check: bool,
     pub simd_ffi: bool,
     pub unmarked_api: bool,
@@ -173,7 +315,11 @@ impl Features {
             unboxed_closures: false,
             rustc_diagnostic_macros: false,
             visible_private_types: false,
-            quote: false,
+            allow_quote: false,
+            allow_asm: false,
+            allow_log_syntax: false,
+            allow_concat_idents: false,
+            allow_trace_macros: false,
             old_orphan_check: false,
             simd_ffi: false,
             unmarked_api: false,
@@ -222,6 +368,18 @@ pub fn emit_feature_warn(diag: &SpanHandler, feature: &str, span: Span, explain:
     }
 }
 
+pub const EXPLAIN_ASM: &'static str =
+    "inline assembly is not stable enough for use and is subject to change";
+
+pub const EXPLAIN_LOG_SYNTAX: &'static str =
+    "`log_syntax!` is not stable enough for use and is subject to change";
+
+pub const EXPLAIN_CONCAT_IDENTS: &'static str =
+    "`concat_idents` is not stable enough for use and is subject to change";
+
+pub const EXPLAIN_TRACE_MACROS: &'static str =
+    "`trace_macros` is not stable enough for use and is subject to change";
+
 struct MacroVisitor<'a> {
     context: &'a Context<'a>
 }
@@ -231,24 +389,28 @@ impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
         let ast::MacInvocTT(ref path, _, _) = mac.node;
         let id = path.segments.last().unwrap().identifier;
 
+        // Issue 22234: If you add a new case here, make sure to also
+        // add code to catch the macro during or after expansion.
+        //
+        // We still keep this MacroVisitor (rather than *solely*
+        // relying on catching cases during or after expansion) to
+        // catch uses of these macros within conditionally-compiled
+        // code, e.g. `#[cfg]`-guarded functions.
+
         if id == token::str_to_ident("asm") {
-            self.context.gate_feature("asm", path.span, "inline assembly is not \
-                stable enough for use and is subject to change");
+            self.context.gate_feature("asm", path.span, EXPLAIN_ASM);
         }
 
         else if id == token::str_to_ident("log_syntax") {
-            self.context.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
-                stable enough for use and is subject to change");
+            self.context.gate_feature("log_syntax", path.span, EXPLAIN_LOG_SYNTAX);
         }
 
         else if id == token::str_to_ident("trace_macros") {
-            self.context.gate_feature("trace_macros", path.span, "`trace_macros` is not \
-                stable enough for use and is subject to change");
+            self.context.gate_feature("trace_macros", path.span, EXPLAIN_TRACE_MACROS);
         }
 
         else if id == token::str_to_ident("concat_idents") {
-            self.context.gate_feature("concat_idents", path.span, "`concat_idents` is not \
-                stable enough for use and is subject to change");
+            self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS);
         }
     }
 }
@@ -274,22 +436,6 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
     }
 
     fn visit_item(&mut self, i: &ast::Item) {
-        for attr in &i.attrs {
-            if attr.name() == "thread_local" {
-                self.gate_feature("thread_local", i.span,
-                                  "`#[thread_local]` is an experimental feature, and does not \
-                                  currently handle destructors. There is no corresponding \
-                                  `#[task_local]` mapping to the task model");
-            } else if attr.name() == "linkage" {
-                self.gate_feature("linkage", i.span,
-                                  "the `linkage` attribute is experimental \
-                                   and not portable across platforms")
-            } else if attr.name() == "rustc_on_unimplemented" {
-                self.gate_feature("on_unimplemented", i.span,
-                                  "the `#[rustc_on_unimplemented]` attribute \
-                                  is an experimental feature")
-            }
-        }
         match i.node {
             ast::ItemExternCrate(_) => {
                 if attr::contains_name(&i.attrs[], "macro_reexport") {
@@ -463,30 +609,27 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
     }
 
     fn visit_attribute(&mut self, attr: &ast::Attribute) {
-        if attr.check_name("staged_api") {
-            self.gate_feature("staged_api", attr.span,
-                              "staged_api is for use by rustc only");
-        } else if attr.check_name("plugin") {
-            self.gate_feature("plugin", attr.span,
-                              "compiler plugins are experimental \
-                               and possibly buggy");
-        }
-
-        if attr::contains_name(slice::ref_slice(attr), "lang") {
-            self.gate_feature("lang_items",
-                              attr.span,
-                              "language items are subject to change");
-        }
-
-        if attr.check_name("no_std") {
-            self.gate_feature("no_std", attr.span,
-                              "no_std is experimental");
+        let name = &*attr.name();
+        for &(n, ty) in KNOWN_ATTRIBUTES {
+            if n == name {
+                if let Gated(gate, desc) = ty {
+                    self.gate_feature(gate, attr.span, desc);
+                }
+                return;
+            }
         }
-
-        if attr.check_name("unsafe_no_drop_flag") {
-            self.gate_feature("unsafe_no_drop_flag", attr.span,
-                              "unsafe_no_drop_flag has unstable semantics \
-                               and may be removed in the future");
+        if name.starts_with("rustc_") {
+            self.gate_feature("rustc_attrs", attr.span,
+                              "unless otherwise specified, attributes \
+                               with the prefix `rustc_` \
+                               are reserved for internal compiler diagnostics");
+        } else {
+            self.gate_feature("custom_attribute", attr.span,
+                       format!("The attribute `{}` is currently \
+                                unknown to the the compiler and \
+                                may have meaning \
+                                added to it in the future",
+                                name).as_slice());
         }
     }
 
@@ -591,11 +734,18 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
 
     check(&mut cx, krate);
 
+    // FIXME (pnkfelix): Before adding the 99th entry below, change it
+    // to a single-pass (instead of N calls to `.has_feature`).
+
     Features {
         unboxed_closures: cx.has_feature("unboxed_closures"),
         rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
         visible_private_types: cx.has_feature("visible_private_types"),
-        quote: cx.has_feature("quote"),
+        allow_quote: cx.has_feature("quote"),
+        allow_asm: cx.has_feature("asm"),
+        allow_log_syntax: cx.has_feature("log_syntax"),
+        allow_concat_idents: cx.has_feature("concat_idents"),
+        allow_trace_macros: cx.has_feature("trace_macros"),
         old_orphan_check: cx.has_feature("old_orphan_check"),
         simd_ffi: cx.has_feature("simd_ffi"),
         unmarked_api: cx.has_feature("unmarked_api"),
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index f4b0c867f42..e8bdcd62b58 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -30,9 +30,9 @@
 #![feature(env)]
 #![feature(hash)]
 #![feature(int_uint)]
-#![feature(io)]
+#![feature(old_io)]
 #![feature(libc)]
-#![feature(path)]
+#![feature(old_path)]
 #![feature(quote, unsafe_destructor)]
 #![feature(rustc_private)]
 #![feature(staged_api)]
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 583095e1574..4b021f2434f 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2761,15 +2761,13 @@ impl<'a> State<'a> {
             ast::LitStr(ref st, style) => self.print_string(&st, style),
             ast::LitByte(byte) => {
                 let mut res = String::from_str("b'");
-                ascii::escape_default(byte, |c| res.push(c as char));
+                res.extend(ascii::escape_default(byte).map(|c| c as char));
                 res.push('\'');
                 word(&mut self.s, &res[])
             }
             ast::LitChar(ch) => {
                 let mut res = String::from_str("'");
-                for c in ch.escape_default() {
-                    res.push(c);
-                }
+                res.extend(ch.escape_default());
                 res.push('\'');
                 word(&mut self.s, &res[])
             }
@@ -2809,8 +2807,8 @@ impl<'a> State<'a> {
             ast::LitBinary(ref v) => {
                 let mut escaped: String = String::new();
                 for &ch in &**v {
-                    ascii::escape_default(ch as u8,
-                                          |ch| escaped.push(ch as char));
+                    escaped.extend(ascii::escape_default(ch as u8)
+                                         .map(|c| c as char));
                 }
                 word(&mut self.s, &format!("b\"{}\"", escaped)[])
             }