about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-09-29 12:35:39 +0000
committerbors <bors@rust-lang.org>2018-09-29 12:35:39 +0000
commit9653f790333d1270f36f1614e85d8a7b54193e75 (patch)
tree854a3f96041f7734a63a7b197ccd968b3674cd9a
parent7e7bc060c1564b19742fd241f40a02c9210e445c (diff)
parentdef5f84fa64f4a64137dcfc4cc5e3007cc9257e6 (diff)
downloadrust-9653f790333d1270f36f1614e85d8a7b54193e75.tar.gz
rust-9653f790333d1270f36f1614e85d8a7b54193e75.zip
Auto merge of #54660 - kennytm:rollup, r=kennytm
Rollup of 8 pull requests

Successful merges:

 - #54564 (Add 1.29.1 release notes)
 - #54567 (Include path in stamp hash for debuginfo tests)
 - #54577 (rustdoc: give proc-macros their own pages)
 - #54590 (std: Don't let `rust_panic` get inlined)
 - #54598 (Remove useless lifetimes from `Pin` `impl`s.)
 - #54604 (Added help message for `self_in_typedefs` feature gate)
 - #54635 (Improve docs for std::io::Seek)
 - #54645 (Compute Android gdb version in compiletest)
-rw-r--r--RELEASES.md13
-rw-r--r--src/libcore/pin.rs10
-rw-r--r--src/librustc_mir/hair/pattern/check_match.rs2
-rw-r--r--src/librustc_resolve/lib.rs38
-rw-r--r--src/librustc_typeck/check/method/probe.rs9
-rw-r--r--src/librustdoc/clean/inline.rs68
-rw-r--r--src/librustdoc/clean/mod.rs36
-rw-r--r--src/librustdoc/doctree.rs14
-rw-r--r--src/librustdoc/html/item_type.rs24
-rw-r--r--src/librustdoc/html/render.rs60
-rw-r--r--src/librustdoc/html/static/main.js5
-rw-r--r--src/librustdoc/html/static/themes/dark.css4
-rw-r--r--src/librustdoc/html/static/themes/light.css4
-rw-r--r--src/librustdoc/passes/mod.rs3
-rw-r--r--src/librustdoc/visit_ast.rs82
-rw-r--r--src/libstd/io/mod.rs4
-rw-r--r--src/libstd/panicking.rs1
-rw-r--r--src/test/rustdoc/inline_cross/auxiliary/proc_macro.rs37
-rw-r--r--src/test/rustdoc/inline_cross/proc_macro.rs27
-rw-r--r--src/test/rustdoc/proc-macro.rs62
-rw-r--r--src/test/ui/feature-gates/feature-gate-self_in_typedefs.stderr2
-rw-r--r--src/test/ui/inference/inference_unstable.stderr2
-rw-r--r--src/tools/compiletest/src/main.rs44
-rw-r--r--src/tools/compiletest/src/runtest.rs397
24 files changed, 671 insertions, 277 deletions
diff --git a/RELEASES.md b/RELEASES.md
index 819c9184364..08470e731d8 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,3 +1,16 @@
+Version 1.29.1 (2018-09-25)
+===========================
+
+Security Notes
+--------------
+
+- The standard library's `str::repeat` function contained an out of bounds write
+  caused by an integer overflow. This has been fixed by deterministically
+  panicking when an overflow happens.
+
+  Thank you to Scott McMurray for responsibily disclosing this vulnerability to
+  us.
+
 Version 1.29.0 (2018-09-13)
 ==========================
 
diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs
index d09a545aecf..0224560af4c 100644
--- a/src/libcore/pin.rs
+++ b/src/libcore/pin.rs
@@ -293,21 +293,21 @@ where
 }
 
 #[unstable(feature = "pin", issue = "49150")]
-impl<'a, P: fmt::Debug> fmt::Debug for Pin<P> {
+impl<P: fmt::Debug> fmt::Debug for Pin<P> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Debug::fmt(&self.pointer, f)
     }
 }
 
 #[unstable(feature = "pin", issue = "49150")]
-impl<'a, P: fmt::Display> fmt::Display for Pin<P> {
+impl<P: fmt::Display> fmt::Display for Pin<P> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Display::fmt(&self.pointer, f)
     }
 }
 
 #[unstable(feature = "pin", issue = "49150")]
-impl<'a, P: fmt::Pointer> fmt::Pointer for Pin<P> {
+impl<P: fmt::Pointer> fmt::Pointer for Pin<P> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Pointer::fmt(&self.pointer, f)
     }
@@ -319,10 +319,10 @@ impl<'a, P: fmt::Pointer> fmt::Pointer for Pin<P> {
 // for other reasons, though, so we just need to take care not to allow such
 // impls to land in std.
 #[unstable(feature = "pin", issue = "49150")]
-impl<'a, P, U> CoerceUnsized<Pin<U>> for Pin<P>
+impl<P, U> CoerceUnsized<Pin<U>> for Pin<P>
 where
     P: CoerceUnsized<U>,
 {}
 
 #[unstable(feature = "pin", issue = "49150")]
-impl<'a, P> Unpin for Pin<P> {}
+impl<P> Unpin for Pin<P> {}
diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs
index 23667d1b331..6187e091319 100644
--- a/src/librustc_mir/hair/pattern/check_match.rs
+++ b/src/librustc_mir/hair/pattern/check_match.rs
@@ -539,7 +539,7 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor,
                 .emit();
         } else if has_guard && !cx.tcx.allow_bind_by_move_patterns_with_guards() {
             let mut err = struct_span_err!(cx.tcx.sess, p.span, E0008,
-                                       "cannot bind by-move into a pattern guard");
+                                           "cannot bind by-move into a pattern guard");
             err.span_label(p.span, "moves value into pattern guard");
             if cx.tcx.sess.opts.unstable_features.is_nightly_build() && cx.tcx.use_mir_borrowck() {
                 err.help("add #![feature(bind_by_move_pattern_guards)] to the \
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 91b0e9c1dca..5e3f7470099 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -42,8 +42,9 @@ use rustc::lint;
 use rustc::hir::def::*;
 use rustc::hir::def::Namespace::*;
 use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, DefId};
-use rustc::ty;
 use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
+use rustc::session::config::nightly_options;
+use rustc::ty;
 use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap};
 
 use rustc_metadata::creader::CrateLoader;
@@ -1381,6 +1382,9 @@ pub struct Resolver<'a, 'b: 'a> {
     /// The current self type if inside an impl (used for better errors).
     current_self_type: Option<Ty>,
 
+    /// The current self item if inside an ADT (used for better errors).
+    current_self_item: Option<NodeId>,
+
     /// The idents for the primitive types.
     primitive_type_table: PrimitiveTypeTable,
 
@@ -1710,6 +1714,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
 
             current_trait_ref: None,
             current_self_type: None,
+            current_self_item: None,
 
             primitive_type_table: PrimitiveTypeTable::new(),
 
@@ -2186,15 +2191,17 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
     }
 
     fn resolve_adt(&mut self, item: &Item, generics: &Generics) {
-        self.with_type_parameter_rib(HasTypeParameters(generics, ItemRibKind), |this| {
-            let item_def_id = this.definitions.local_def_id(item.id);
-            if this.session.features_untracked().self_in_typedefs {
-                this.with_self_rib(Def::SelfTy(None, Some(item_def_id)), |this| {
+        self.with_current_self_item(item, |this| {
+            this.with_type_parameter_rib(HasTypeParameters(generics, ItemRibKind), |this| {
+                let item_def_id = this.definitions.local_def_id(item.id);
+                if this.session.features_untracked().self_in_typedefs {
+                    this.with_self_rib(Def::SelfTy(None, Some(item_def_id)), |this| {
+                        visit::walk_item(this, item);
+                    });
+                } else {
                     visit::walk_item(this, item);
-                });
-            } else {
-                visit::walk_item(this, item);
-            }
+                }
+            });
         });
     }
 
@@ -2435,6 +2442,15 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
         result
     }
 
+    fn with_current_self_item<T, F>(&mut self, self_item: &Item, f: F) -> T
+        where F: FnOnce(&mut Resolver) -> T
+    {
+        let previous_value = replace(&mut self.current_self_item, Some(self_item.id));
+        let result = f(self);
+        self.current_self_item = previous_value;
+        result
+    }
+
     /// This is called to resolve a trait reference from an `impl` (i.e. `impl Trait for Foo`)
     fn with_optional_trait_ref<T, F>(&mut self, opt_trait_ref: Option<&TraitRef>, f: F) -> T
         where F: FnOnce(&mut Resolver, Option<DefId>) -> T
@@ -3004,6 +3020,10 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
                     "traits and impls"
                 };
                 err.span_label(span, format!("`Self` is only available in {}", available_in));
+                if this.current_self_item.is_some() && nightly_options::is_nightly_build() {
+                    err.help("add #![feature(self_in_typedefs)] to the crate attributes \
+                              to enable");
+                }
                 return (err, Vec::new());
             }
             if is_self_value(path, ns) {
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 4af3790450c..ec4483204f0 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -17,6 +17,9 @@ use check::FnCtxt;
 use hir::def_id::DefId;
 use hir::def::Def;
 use namespace::Namespace;
+use rustc::hir;
+use rustc::lint;
+use rustc::session::config::nightly_options;
 use rustc::ty::subst::{Subst, Substs};
 use rustc::traits::{self, ObligationCause};
 use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable};
@@ -28,8 +31,6 @@ use rustc::middle::stability;
 use syntax::ast;
 use syntax::util::lev_distance::{lev_distance, find_best_match_for_name};
 use syntax_pos::{Span, symbol::Symbol};
-use rustc::hir;
-use rustc::lint;
 use std::mem;
 use std::ops::Deref;
 use std::rc::Rc;
@@ -1073,9 +1074,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
             self.tcx.item_path_str(stable_pick.item.def_id),
         ));
 
-        if ::rustc::session::config::nightly_options::is_nightly_build() {
+        if nightly_options::is_nightly_build() {
             for (candidate, feature) in unstable_candidates {
-                diag.note(&format!(
+                diag.help(&format!(
                     "add #![feature({})] to the crate attributes to enable `{}`",
                     feature,
                     self.tcx.item_path_str(candidate.item.def_id),
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 1ea130cf16a..a435712ac3d 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -13,7 +13,7 @@
 use std::iter::once;
 
 use syntax::ast;
-use syntax::ext::base::MacroKind;
+use syntax::ext::base::{MacroKind, SyntaxExtension};
 use syntax_pos::Span;
 
 use rustc::hir;
@@ -105,12 +105,12 @@ pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name, visited: &mut FxHa
             record_extern_fqn(cx, did, clean::TypeKind::Const);
             clean::ConstantItem(build_const(cx, did))
         }
-        // FIXME(misdreavus): if attributes/derives come down here we should probably document them
-        // separately
+        // FIXME: proc-macros don't propagate attributes or spans across crates, so they look empty
         Def::Macro(did, MacroKind::Bang) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Macro);
-            if let Some(mac) = build_macro(cx, did, name) {
-                clean::MacroItem(mac)
+            let mac = build_macro(cx, did, name);
+            if let clean::MacroItem(..) = mac {
+                record_extern_fqn(cx, did, clean::TypeKind::Macro);
+                mac
             } else {
                 return None;
             }
@@ -442,31 +442,41 @@ fn build_static(cx: &DocContext, did: DefId, mutable: bool) -> clean::Static {
     }
 }
 
-fn build_macro(cx: &DocContext, did: DefId, name: ast::Name) -> Option<clean::Macro> {
+fn build_macro(cx: &DocContext, did: DefId, name: ast::Name) -> clean::ItemEnum {
     let imported_from = cx.tcx.original_crate_name(did.krate);
-    let def = match cx.cstore.load_macro_untracked(did, cx.sess()) {
-        LoadedMacro::MacroDef(macro_def) => macro_def,
-        // FIXME(jseyfried): document proc macro re-exports
-        LoadedMacro::ProcMacro(..) => return None,
-    };
-
-    let matchers: hir::HirVec<Span> = if let ast::ItemKind::MacroDef(ref def) = def.node {
-        let tts: Vec<_> = def.stream().into_trees().collect();
-        tts.chunks(4).map(|arm| arm[0].span()).collect()
-    } else {
-        unreachable!()
-    };
-
-    let source = format!("macro_rules! {} {{\n{}}}",
-                         name.clean(cx),
-                         matchers.iter().map(|span| {
-                             format!("    {} => {{ ... }};\n", span.to_src(cx))
-                         }).collect::<String>());
+    match cx.cstore.load_macro_untracked(did, cx.sess()) {
+        LoadedMacro::MacroDef(def) => {
+            let matchers: hir::HirVec<Span> = if let ast::ItemKind::MacroDef(ref def) = def.node {
+                let tts: Vec<_> = def.stream().into_trees().collect();
+                tts.chunks(4).map(|arm| arm[0].span()).collect()
+            } else {
+                unreachable!()
+            };
+
+            let source = format!("macro_rules! {} {{\n{}}}",
+                                 name.clean(cx),
+                                 matchers.iter().map(|span| {
+                                     format!("    {} => {{ ... }};\n", span.to_src(cx))
+                                 }).collect::<String>());
+
+            clean::MacroItem(clean::Macro {
+                source,
+                imported_from: Some(imported_from).clean(cx),
+            })
+        }
+        LoadedMacro::ProcMacro(ext) => {
+            let helpers = match &*ext {
+                &SyntaxExtension::ProcMacroDerive(_, ref syms, ..) => { syms.clean(cx) }
+                _ => Vec::new(),
+            };
+
+            clean::ProcMacroItem(clean::ProcMacro {
+                kind: ext.kind(),
+                helpers,
+            })
+        }
+    }
 
-    Some(clean::Macro {
-        source,
-        imported_from: Some(imported_from).clean(cx),
-    })
 }
 
 /// A trait's generics clause actually contains all of the predicates for all of
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 537905c3008..a91f2fd7474 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -21,6 +21,7 @@ pub use self::Visibility::{Public, Inherited};
 use rustc_target::spec::abi::Abi;
 use syntax::ast::{self, AttrStyle, Ident};
 use syntax::attr;
+use syntax::ext::base::MacroKind;
 use syntax::source_map::{dummy_spanned, Spanned};
 use syntax::ptr::P;
 use syntax::symbol::keywords::{self, Keyword};
@@ -527,6 +528,7 @@ pub enum ItemEnum {
     /// `type`s from an extern block
     ForeignTypeItem,
     MacroItem(Macro),
+    ProcMacroItem(ProcMacro),
     PrimitiveItem(PrimitiveType),
     AssociatedConstItem(Type, Option<String>),
     AssociatedTypeItem(Vec<GenericBound>, Option<Type>),
@@ -588,6 +590,7 @@ impl Clean<Item> for doctree::Module {
         items.extend(self.traits.iter().map(|x| x.clean(cx)));
         items.extend(self.impls.iter().flat_map(|x| x.clean(cx)));
         items.extend(self.macros.iter().map(|x| x.clean(cx)));
+        items.extend(self.proc_macros.iter().map(|x| x.clean(cx)));
 
         // determine if we should display the inner contents or
         // the outer `mod` item for the source code.
@@ -2191,6 +2194,8 @@ pub enum TypeKind {
     Typedef,
     Foreign,
     Macro,
+    Attr,
+    Derive,
 }
 
 pub trait GetDefId {
@@ -3727,7 +3732,12 @@ pub fn register_def(cx: &DocContext, def: Def) -> DefId {
         Def::Static(i, _) => (i, TypeKind::Static),
         Def::Variant(i) => (cx.tcx.parent_def_id(i).expect("cannot get parent def id"),
                             TypeKind::Enum),
-        Def::Macro(i, _) => (i, TypeKind::Macro),
+        Def::Macro(i, mac_kind) => match mac_kind {
+            MacroKind::Bang => (i, TypeKind::Macro),
+            MacroKind::Attr => (i, TypeKind::Attr),
+            MacroKind::Derive => (i, TypeKind::Derive),
+            MacroKind::ProcMacroStub => unreachable!(),
+        },
         Def::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait),
         Def::SelfTy(_, Some(impl_def_id)) => {
             return impl_def_id
@@ -3783,6 +3793,30 @@ impl Clean<Item> for doctree::Macro {
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
+pub struct ProcMacro {
+    pub kind: MacroKind,
+    pub helpers: Vec<String>,
+}
+
+impl Clean<Item> for doctree::ProcMacro {
+    fn clean(&self, cx: &DocContext) -> Item {
+        Item {
+            name: Some(self.name.clean(cx)),
+            attrs: self.attrs.clean(cx),
+            source: self.whence.clean(cx),
+            visibility: Some(Public),
+            stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
+            def_id: cx.tcx.hir.local_def_id(self.id),
+            inner: ProcMacroItem(ProcMacro {
+                kind: self.kind,
+                helpers: self.helpers.clean(cx),
+            }),
+        }
+    }
+}
+
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Stability {
     pub level: stability::StabilityLevel,
     pub feature: String,
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index dd1e1e99957..4a6a4ee09ea 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -15,6 +15,7 @@ pub use self::StructType::*;
 use syntax::ast;
 use syntax::ast::{Name, NodeId};
 use syntax::attr;
+use syntax::ext::base::MacroKind;
 use syntax::ptr::P;
 use syntax::source_map::Spanned;
 use syntax_pos::{self, Span};
@@ -46,6 +47,7 @@ pub struct Module {
     pub impls: Vec<Impl>,
     pub foreigns: Vec<hir::ForeignMod>,
     pub macros: Vec<Macro>,
+    pub proc_macros: Vec<ProcMacro>,
     pub is_crate: bool,
 }
 
@@ -75,6 +77,7 @@ impl Module {
             impls      : Vec::new(),
             foreigns   : Vec::new(),
             macros     : Vec::new(),
+            proc_macros: Vec::new(),
             is_crate   : false,
         }
     }
@@ -264,6 +267,17 @@ pub struct Import {
     pub whence: Span,
 }
 
+pub struct ProcMacro {
+    pub name: Name,
+    pub id: NodeId,
+    pub kind: MacroKind,
+    pub helpers: Vec<Name>,
+    pub attrs: hir::HirVec<ast::Attribute>,
+    pub whence: Span,
+    pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
+}
+
 pub fn struct_type_from_def(vdata: &hir::VariantData) -> StructType {
     match *vdata {
         hir::VariantData::Struct(..) => Plain,
diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs
index a5131e327e0..acb8f6a66df 100644
--- a/src/librustdoc/html/item_type.rs
+++ b/src/librustdoc/html/item_type.rs
@@ -11,6 +11,7 @@
 //! Item types.
 
 use std::fmt;
+use syntax::ext::base::MacroKind;
 use clean;
 
 /// Item type. Corresponds to `clean::ItemEnum` variants.
@@ -19,6 +20,11 @@ use clean;
 /// discriminants. JavaScript then is used to decode them into the original value.
 /// Consequently, every change to this type should be synchronized to
 /// the `itemTypes` mapping table in `static/main.js`.
+///
+/// In addition, code in `html::render` uses this enum to generate CSS classes, page prefixes, and
+/// module headings. If you are adding to this enum and want to ensure that the sidebar also prints
+/// a heading, edit the listing in `html/render.rs`, function `sidebar_module`. This uses an
+/// ordering based on a helper function inside `item_module`, in the same file.
 #[derive(Copy, PartialEq, Clone, Debug)]
 pub enum ItemType {
     Module          = 0,
@@ -44,6 +50,8 @@ pub enum ItemType {
     ForeignType     = 20,
     Keyword         = 21,
     Existential     = 22,
+    ProcAttribute   = 23,
+    ProcDerive      = 24,
 }
 
 
@@ -88,6 +96,12 @@ impl<'a> From<&'a clean::Item> for ItemType {
             clean::AssociatedTypeItem(..)  => ItemType::AssociatedType,
             clean::ForeignTypeItem         => ItemType::ForeignType,
             clean::KeywordItem(..)         => ItemType::Keyword,
+            clean::ProcMacroItem(ref mac)  => match mac.kind {
+                MacroKind::Bang            => ItemType::Macro,
+                MacroKind::Attr            => ItemType::ProcAttribute,
+                MacroKind::Derive          => ItemType::ProcDerive,
+                MacroKind::ProcMacroStub   => unreachable!(),
+            }
             clean::StrippedItem(..)        => unreachable!(),
         }
     }
@@ -107,7 +121,9 @@ impl From<clean::TypeKind> for ItemType {
             clean::TypeKind::Variant  => ItemType::Variant,
             clean::TypeKind::Typedef  => ItemType::Typedef,
             clean::TypeKind::Foreign  => ItemType::ForeignType,
-            clean::TypeKind::Macro  => ItemType::Macro,
+            clean::TypeKind::Macro    => ItemType::Macro,
+            clean::TypeKind::Attr     => ItemType::ProcAttribute,
+            clean::TypeKind::Derive   => ItemType::ProcDerive,
         }
     }
 }
@@ -138,6 +154,8 @@ impl ItemType {
             ItemType::ForeignType     => "foreigntype",
             ItemType::Keyword         => "keyword",
             ItemType::Existential     => "existential",
+            ItemType::ProcAttribute   => "attr",
+            ItemType::ProcDerive      => "derive",
         }
     }
 
@@ -166,7 +184,9 @@ impl ItemType {
             ItemType::Constant |
             ItemType::AssociatedConst => NameSpace::Value,
 
-            ItemType::Macro => NameSpace::Macro,
+            ItemType::Macro |
+            ItemType::ProcAttribute |
+            ItemType::ProcDerive => NameSpace::Macro,
 
             ItemType::Keyword => NameSpace::Keyword,
         }
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 3e1720f8b8a..1c61e73fae0 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -56,6 +56,7 @@ use externalfiles::ExternalHtml;
 
 use serialize::json::{ToJson, Json, as_json};
 use syntax::ast;
+use syntax::ext::base::MacroKind;
 use syntax::source_map::FileName;
 use syntax::feature_gate::UnstableFeatures;
 use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId};
@@ -1595,6 +1596,8 @@ struct AllTypes {
     statics: FxHashSet<ItemEntry>,
     constants: FxHashSet<ItemEntry>,
     keywords: FxHashSet<ItemEntry>,
+    attributes: FxHashSet<ItemEntry>,
+    derives: FxHashSet<ItemEntry>,
 }
 
 impl AllTypes {
@@ -1613,6 +1616,8 @@ impl AllTypes {
             statics: new_set(100),
             constants: new_set(100),
             keywords: new_set(100),
+            attributes: new_set(100),
+            derives: new_set(100),
         }
     }
 
@@ -1634,6 +1639,8 @@ impl AllTypes {
                 ItemType::Existential => self.existentials.insert(ItemEntry::new(new_url, name)),
                 ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
                 ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
+                ItemType::ProcAttribute => self.attributes.insert(ItemEntry::new(new_url, name)),
+                ItemType::ProcDerive => self.derives.insert(ItemEntry::new(new_url, name)),
                 _ => true,
             };
         }
@@ -1673,6 +1680,8 @@ impl fmt::Display for AllTypes {
         print_entries(f, &self.primitives, "Primitives", "primitives")?;
         print_entries(f, &self.traits, "Traits", "traits")?;
         print_entries(f, &self.macros, "Macros", "macros")?;
+        print_entries(f, &self.attributes, "Attribute Macros", "attributes")?;
+        print_entries(f, &self.derives, "Derive Macros", "derives")?;
         print_entries(f, &self.functions, "Functions", "functions")?;
         print_entries(f, &self.typedefs, "Typedefs", "typedefs")?;
         print_entries(f, &self.existentials, "Existentials", "existentials")?;
@@ -2155,6 +2164,12 @@ impl<'a> fmt::Display for Item<'a> {
             clean::EnumItem(..) => write!(fmt, "Enum ")?,
             clean::TypedefItem(..) => write!(fmt, "Type Definition ")?,
             clean::MacroItem(..) => write!(fmt, "Macro ")?,
+            clean::ProcMacroItem(ref mac) => match mac.kind {
+                MacroKind::Bang => write!(fmt, "Macro ")?,
+                MacroKind::Attr => write!(fmt, "Attribute Macro ")?,
+                MacroKind::Derive => write!(fmt, "Derive Macro ")?,
+                MacroKind::ProcMacroStub => unreachable!(),
+            }
             clean::PrimitiveItem(..) => write!(fmt, "Primitive Type ")?,
             clean::StaticItem(..) | clean::ForeignStaticItem(..) => write!(fmt, "Static ")?,
             clean::ConstantItem(..) => write!(fmt, "Constant ")?,
@@ -2191,6 +2206,7 @@ impl<'a> fmt::Display for Item<'a> {
             clean::EnumItem(ref e) => item_enum(fmt, self.cx, self.item, e),
             clean::TypedefItem(ref t, _) => item_typedef(fmt, self.cx, self.item, t),
             clean::MacroItem(ref m) => item_macro(fmt, self.cx, self.item, m),
+            clean::ProcMacroItem(ref m) => item_proc_macro(fmt, self.cx, self.item, m),
             clean::PrimitiveItem(ref p) => item_primitive(fmt, self.cx, self.item, p),
             clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) =>
                 item_static(fmt, self.cx, self.item, i),
@@ -4079,11 +4095,12 @@ impl<'a> fmt::Display for Sidebar<'a> {
                 write!(fmt,
                        "<div class='block version'>\
                         <p>Version {}</p>\
-                        </div>
-                        <a id='all-types' href='all.html'><p>See all {}'s items</p></a>",
-                       version,
-                       it.name.as_ref().unwrap())?;
+                        </div>",
+                       version)?;
             }
+
+            write!(fmt, "<a id='all-types' href='all.html'><p>See all {}'s items</p></a>",
+                   it.name.as_ref().expect("crates always have a name"))?;
         }
 
         write!(fmt, "<div class=\"sidebar-elems\">")?;
@@ -4523,6 +4540,8 @@ fn item_ty_to_strs(ty: &ItemType) -> (&'static str, &'static str) {
         ItemType::ForeignType     => ("foreign-types", "Foreign Types"),
         ItemType::Keyword         => ("keywords", "Keywords"),
         ItemType::Existential     => ("existentials", "Existentials"),
+        ItemType::ProcAttribute   => ("attributes", "Attribute Macros"),
+        ItemType::ProcDerive      => ("derives", "Derive Macros"),
     }
 }
 
@@ -4598,6 +4617,39 @@ fn item_macro(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
     document(w, cx, it)
 }
 
+fn item_proc_macro(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, m: &clean::ProcMacro)
+    -> fmt::Result
+{
+    let name = it.name.as_ref().expect("proc-macros always have names");
+    match m.kind {
+        MacroKind::Bang => {
+            write!(w, "<pre class='rust macro'>")?;
+            write!(w, "{}!() {{ /* proc-macro */ }}", name)?;
+            write!(w, "</pre>")?;
+        }
+        MacroKind::Attr => {
+            write!(w, "<pre class='rust attr'>")?;
+            write!(w, "#[{}]", name)?;
+            write!(w, "</pre>")?;
+        }
+        MacroKind::Derive => {
+            write!(w, "<pre class='rust derive'>")?;
+            write!(w, "#[derive({})]", name)?;
+            if !m.helpers.is_empty() {
+                writeln!(w, "\n{{")?;
+                writeln!(w, "    // Attributes available to this derive:")?;
+                for attr in &m.helpers {
+                    writeln!(w, "    #[{}]", attr)?;
+                }
+                write!(w, "}}")?;
+            }
+            write!(w, "</pre>")?;
+        }
+        _ => {}
+    }
+    document(w, cx, it)
+}
+
 fn item_primitive(w: &mut fmt::Formatter, cx: &Context,
                   it: &clean::Item,
                   _p: &clean::PrimitiveType) -> fmt::Result {
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index 0b56692bc2e..6307dda454d 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -39,7 +39,10 @@
                      "associatedconstant",
                      "union",
                      "foreigntype",
-                     "keyword"];
+                     "keyword",
+                     "existential",
+                     "attr",
+                     "derive"];
 
     var search_input = document.getElementsByClassName('search-input')[0];
 
diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css
index 12d22084893..34a1d71beec 100644
--- a/src/librustdoc/html/static/themes/dark.css
+++ b/src/librustdoc/html/static/themes/dark.css
@@ -124,6 +124,8 @@ pre {
 .content .highlighted.tymethod { background-color: #4950ed; }
 .content .highlighted.type { background-color: #38902c; }
 .content .highlighted.foreigntype { background-color: #b200d6; }
+.content .highlighted.attr,
+.content .highlighted.derive,
 .content .highlighted.macro { background-color: #217d1c; }
 .content .highlighted.constant,
 .content .highlighted.static { background-color: #0063cc; }
@@ -134,6 +136,8 @@ pre {
 .content span.struct, .content a.struct, .block a.current.struct { color: #2dbfb8; }
 .content span.type, .content a.type, .block a.current.type { color: #ff7f00; }
 .content span.foreigntype, .content a.foreigntype, .block a.current.foreigntype { color: #dd7de8; }
+.content span.attr, .content a.attr, .block a.current.attr,
+.content span.derive, .content a.derive, .block a.current.derive,
 .content span.macro, .content a.macro, .block a.current.macro { color: #09bd00; }
 .content span.union, .content a.union, .block a.current.union { color: #a6ae37; }
 .content span.constant, .content a.constant, .block a.current.constant,
diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css
index 043d7ae23c2..8218b1b371e 100644
--- a/src/librustdoc/html/static/themes/light.css
+++ b/src/librustdoc/html/static/themes/light.css
@@ -124,6 +124,8 @@ pre {
 .content .highlighted.tymethod { background-color: #c6afb3; }
 .content .highlighted.type { background-color: #ffc891; }
 .content .highlighted.foreigntype { background-color: #f5c4ff; }
+.content .highlighted.attr,
+.content .highlighted.derive,
 .content .highlighted.macro { background-color: #8ce488; }
 .content .highlighted.constant,
 .content .highlighted.static { background-color: #c3e0ff; }
@@ -134,6 +136,8 @@ pre {
 .content span.struct, .content a.struct, .block a.current.struct { color: #ad448e; }
 .content span.type, .content a.type, .block a.current.type { color: #ba5d00; }
 .content span.foreigntype, .content a.foreigntype, .block a.current.foreigntype { color: #cd00e2; }
+.content span.attr, .content a.attr, .block a.current.attr,
+.content span.derive, .content a.derive, .block a.current.derive,
 .content span.macro, .content a.macro, .block a.current.macro { color: #068000; }
 .content span.union, .content a.union, .block a.current.union { color: #767b27; }
 .content span.constant, .content a.constant, .block a.current.constant,
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 24fec62dd57..d00eb3257d4 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -249,6 +249,9 @@ impl<'a> fold::DocFolder for Stripper<'a> {
             // tymethods/macros have no control over privacy
             clean::MacroItem(..) | clean::TyMethodItem(..) => {}
 
+            // Proc-macros are always public
+            clean::ProcMacroItem(..) => {}
+
             // Primitives are never stripped
             clean::PrimitiveItem(..) => {}
 
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 0e12fd34eb7..92d8dbed071 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -15,6 +15,7 @@ use std::mem;
 
 use syntax::ast;
 use syntax::attr;
+use syntax::ext::base::MacroKind;
 use syntax::source_map::Spanned;
 use syntax_pos::{self, Span};
 
@@ -168,24 +169,75 @@ impl<'a, 'tcx, 'rcx, 'cstore> RustdocVisitor<'a, 'tcx, 'rcx, 'cstore> {
         }
     }
 
-    pub fn visit_fn(&mut self, item: &hir::Item,
+    pub fn visit_fn(&mut self, om: &mut Module, item: &hir::Item,
                     name: ast::Name, fd: &hir::FnDecl,
                     header: hir::FnHeader,
                     gen: &hir::Generics,
-                    body: hir::BodyId) -> Function {
+                    body: hir::BodyId) {
         debug!("Visiting fn");
-        Function {
-            id: item.id,
-            vis: item.vis.clone(),
-            stab: self.stability(item.id),
-            depr: self.deprecation(item.id),
-            attrs: item.attrs.clone(),
-            decl: fd.clone(),
-            name,
-            whence: item.span,
-            generics: gen.clone(),
-            header,
-            body,
+        let macro_kind = item.attrs.iter().filter_map(|a| {
+            if a.check_name("proc_macro") {
+                Some(MacroKind::Bang)
+            } else if a.check_name("proc_macro_derive") {
+                Some(MacroKind::Derive)
+            } else if a.check_name("proc_macro_attribute") {
+                Some(MacroKind::Attr)
+            } else {
+                None
+            }
+        }).next();
+        match macro_kind {
+            Some(kind) => {
+                let name = if kind == MacroKind::Derive {
+                    item.attrs.lists("proc_macro_derive")
+                              .filter_map(|mi| mi.name())
+                              .next()
+                              .expect("proc-macro derives require a name")
+                } else {
+                    name
+                };
+
+                let mut helpers = Vec::new();
+                for mi in item.attrs.lists("proc_macro_derive") {
+                    if !mi.check_name("attributes") {
+                        continue;
+                    }
+
+                    if let Some(list) = mi.meta_item_list() {
+                        for inner_mi in list {
+                            if let Some(name) = inner_mi.name() {
+                                helpers.push(name);
+                            }
+                        }
+                    }
+                }
+
+                om.proc_macros.push(ProcMacro {
+                    name,
+                    id: item.id,
+                    kind,
+                    helpers,
+                    attrs: item.attrs.clone(),
+                    whence: item.span,
+                    stab: self.stability(item.id),
+                    depr: self.deprecation(item.id),
+                });
+            }
+            None => {
+                om.fns.push(Function {
+                    id: item.id,
+                    vis: item.vis.clone(),
+                    stab: self.stability(item.id),
+                    depr: self.deprecation(item.id),
+                    attrs: item.attrs.clone(),
+                    decl: fd.clone(),
+                    name,
+                    whence: item.span,
+                    generics: gen.clone(),
+                    header,
+                    body,
+                });
+            }
         }
     }
 
@@ -425,7 +477,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> RustdocVisitor<'a, 'tcx, 'rcx, 'cstore> {
             hir::ItemKind::Union(ref sd, ref gen) =>
                 om.unions.push(self.visit_union_data(item, name, sd, gen)),
             hir::ItemKind::Fn(ref fd, header, ref gen, body) =>
-                om.fns.push(self.visit_fn(item, name, &**fd, header, gen, body)),
+                self.visit_fn(om, item, name, &**fd, header, gen, body),
             hir::ItemKind::Ty(ref ty, ref gen) => {
                 let t = Typedef {
                     ty: ty.clone(),
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
index 278ee7951b3..e263db24fc2 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -1204,8 +1204,8 @@ pub trait Write {
 pub trait Seek {
     /// Seek to an offset, in bytes, in a stream.
     ///
-    /// A seek beyond the end of a stream is allowed, but implementation
-    /// defined.
+    /// A seek beyond the end of a stream is allowed, but behavior is defined
+    /// by the implementation.
     ///
     /// If the seek operation completed successfully,
     /// this method returns the new position from the start of the stream.
diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs
index 6eb2db8e63b..f79c986cc89 100644
--- a/src/libstd/panicking.rs
+++ b/src/libstd/panicking.rs
@@ -517,6 +517,7 @@ pub fn update_count_then_panic(msg: Box<dyn Any + Send>) -> ! {
 }
 
 /// A private no-mangle function on which to slap yer breakpoints.
+#[inline(never)]
 #[no_mangle]
 #[allow(private_no_mangle_fns)] // yes we get it, but we like breakpoints
 pub fn rust_panic(mut msg: &mut dyn BoxMeUp) -> ! {
diff --git a/src/test/rustdoc/inline_cross/auxiliary/proc_macro.rs b/src/test/rustdoc/inline_cross/auxiliary/proc_macro.rs
new file mode 100644
index 00000000000..6aac070c45b
--- /dev/null
+++ b/src/test/rustdoc/inline_cross/auxiliary/proc_macro.rs
@@ -0,0 +1,37 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![crate_type="proc-macro"]
+#![crate_name="some_macros"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+/// a proc-macro that swallows its input and does nothing.
+#[proc_macro]
+pub fn some_proc_macro(_input: TokenStream) -> TokenStream {
+    TokenStream::new()
+}
+
+/// a proc-macro attribute that passes its item through verbatim.
+#[proc_macro_attribute]
+pub fn some_proc_attr(_attr: TokenStream, item: TokenStream) -> TokenStream {
+    item
+}
+
+/// a derive attribute that adds nothing to its input.
+#[proc_macro_derive(SomeDerive)]
+pub fn some_derive(_item: TokenStream) -> TokenStream {
+    TokenStream::new()
+}
+
diff --git a/src/test/rustdoc/inline_cross/proc_macro.rs b/src/test/rustdoc/inline_cross/proc_macro.rs
new file mode 100644
index 00000000000..a879258f82a
--- /dev/null
+++ b/src/test/rustdoc/inline_cross/proc_macro.rs
@@ -0,0 +1,27 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-stage1
+// aux-build:proc_macro.rs
+// build-aux-docs
+
+// FIXME: if/when proc-macros start exporting their doc attributes across crates, we can turn on
+// cross-crate inlining for them
+
+extern crate some_macros;
+
+// @has proc_macro/index.html
+// @has - '//a/@href' '../some_macros/macro.some_proc_macro.html'
+// @has - '//a/@href' '../some_macros/attr.some_proc_attr.html'
+// @has - '//a/@href' '../some_macros/derive.SomeDerive.html'
+// @!has proc_macro/macro.some_proc_macro.html
+// @!has proc_macro/attr.some_proc_attr.html
+// @!has proc_macro/derive.SomeDerive.html
+pub use some_macros::{some_proc_macro, some_proc_attr, SomeDerive};
diff --git a/src/test/rustdoc/proc-macro.rs b/src/test/rustdoc/proc-macro.rs
new file mode 100644
index 00000000000..bfd194701c8
--- /dev/null
+++ b/src/test/rustdoc/proc-macro.rs
@@ -0,0 +1,62 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-stage1
+
+#![crate_type="proc-macro"]
+#![crate_name="some_macros"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+// @has some_macros/index.html
+// @has - '//h2' 'Macros'
+// @has - '//h2' 'Attribute Macros'
+// @has - '//h2' 'Derive Macros'
+// @!has - '//h2' 'Functions'
+
+// @has some_macros/all.html
+// @has - '//a[@href="macro.some_proc_macro.html"]' 'some_proc_macro'
+// @has - '//a[@href="attr.some_proc_attr.html"]' 'some_proc_attr'
+// @has - '//a[@href="derive.SomeDerive.html"]' 'SomeDerive'
+// @!has - '//a/@href' 'fn.some_proc_macro.html'
+// @!has - '//a/@href' 'fn.some_proc_attr.html'
+// @!has - '//a/@href' 'fn.some_derive.html'
+
+// @has some_macros/index.html '//a/@href' 'macro.some_proc_macro.html'
+// @!has - '//a/@href' 'fn.some_proc_macro.html'
+// @has some_macros/macro.some_proc_macro.html
+// @!has some_macros/fn.some_proc_macro.html
+/// a proc-macro that swallows its input and does nothing.
+#[proc_macro]
+pub fn some_proc_macro(_input: TokenStream) -> TokenStream {
+    TokenStream::new()
+}
+
+// @has some_macros/index.html '//a/@href' 'attr.some_proc_attr.html'
+// @!has - '//a/@href' 'fn.some_proc_attr.html'
+// @has some_macros/attr.some_proc_attr.html
+// @!has some_macros/fn.some_proc_attr.html
+/// a proc-macro attribute that passes its item through verbatim.
+#[proc_macro_attribute]
+pub fn some_proc_attr(_attr: TokenStream, item: TokenStream) -> TokenStream {
+    item
+}
+
+// @has some_macros/index.html '//a/@href' 'derive.SomeDerive.html'
+// @!has - '//a/@href' 'fn.some_derive.html'
+// @has some_macros/derive.SomeDerive.html
+// @!has some_macros/fn.some_derive.html
+/// a derive attribute that adds nothing to its input.
+#[proc_macro_derive(SomeDerive)]
+pub fn some_derive(_item: TokenStream) -> TokenStream {
+    TokenStream::new()
+}
diff --git a/src/test/ui/feature-gates/feature-gate-self_in_typedefs.stderr b/src/test/ui/feature-gates/feature-gate-self_in_typedefs.stderr
index 22ca92bbe13..ab04953f3e5 100644
--- a/src/test/ui/feature-gates/feature-gate-self_in_typedefs.stderr
+++ b/src/test/ui/feature-gates/feature-gate-self_in_typedefs.stderr
@@ -3,6 +3,8 @@ error[E0411]: cannot find type `Self` in this scope
    |
 LL |     Cons(T, &'a Self)
    |                 ^^^^ `Self` is only available in traits and impls
+   |
+   = help: add #![feature(self_in_typedefs)] to the crate attributes to enable
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/inference/inference_unstable.stderr b/src/test/ui/inference/inference_unstable.stderr
index 3a5cb6f2b2e..2851af4891e 100644
--- a/src/test/ui/inference/inference_unstable.stderr
+++ b/src/test/ui/inference/inference_unstable.stderr
@@ -8,5 +8,5 @@ LL |     assert_eq!('x'.ipu_flatten(), 1);
    = warning: once this method is added to the standard library, the ambiguity may cause an error or change in behavior!
    = note: for more information, see issue #48919 <https://github.com/rust-lang/rust/issues/48919>
    = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_flatten(...)` to keep using the current method
-   = note: add #![feature(ipu_flatten)] to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_flatten`
+   = help: add #![feature(ipu_flatten)] to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_flatten`
 
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index a5cf45baa65..2fa459bec94 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -278,7 +278,10 @@ pub fn parse_config(args: Vec<String>) -> Config {
         }
     }
 
-    let (gdb, gdb_version, gdb_native_rust) = analyze_gdb(matches.opt_str("gdb"));
+    let target = opt_str2(matches.opt_str("target"));
+    let android_cross_path = opt_path(matches, "android-cross-path");
+    let (gdb, gdb_version, gdb_native_rust) = analyze_gdb(matches.opt_str("gdb"), &target,
+                                                          &android_cross_path);
 
     let color = match matches.opt_str("color").as_ref().map(|x| &**x) {
         Some("auto") | None => ColorConfig::AutoColor,
@@ -318,7 +321,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         runtool: matches.opt_str("runtool"),
         host_rustcflags: matches.opt_str("host-rustcflags"),
         target_rustcflags: matches.opt_str("target-rustcflags"),
-        target: opt_str2(matches.opt_str("target")),
+        target: target,
         host: opt_str2(matches.opt_str("host")),
         gdb,
         gdb_version,
@@ -326,7 +329,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
         llvm_version: matches.opt_str("llvm-version"),
         system_llvm: matches.opt_present("system-llvm"),
-        android_cross_path: opt_path(matches, "android-cross-path"),
+        android_cross_path: android_cross_path,
         adb_path: opt_str2(matches.opt_str("adb-path")),
         adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
         adb_device_status: opt_str2(matches.opt_str("target")).contains("android")
@@ -780,8 +783,18 @@ fn make_test_closure(
     }))
 }
 
+/// Returns true if the given target is an Android target for the
+/// purposes of GDB testing.
+fn is_android_gdb_target(target: &String) -> bool {
+    match &target[..] {
+        "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => true,
+        _ => false,
+    }
+}
+
 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
-fn analyze_gdb(gdb: Option<String>) -> (Option<String>, Option<u32>, bool) {
+fn analyze_gdb(gdb: Option<String>, target: &String, android_cross_path: &PathBuf)
+               -> (Option<String>, Option<u32>, bool) {
     #[cfg(not(windows))]
     const GDB_FALLBACK: &str = "gdb";
     #[cfg(windows)]
@@ -789,14 +802,27 @@ fn analyze_gdb(gdb: Option<String>) -> (Option<String>, Option<u32>, bool) {
 
     const MIN_GDB_WITH_RUST: u32 = 7011010;
 
+    let fallback_gdb = || {
+        if is_android_gdb_target(target) {
+            let mut gdb_path = match android_cross_path.to_str() {
+                Some(x) => x.to_owned(),
+                None => panic!("cannot find android cross path"),
+            };
+            gdb_path.push_str("/bin/gdb");
+            gdb_path
+        } else {
+            GDB_FALLBACK.to_owned()
+        }
+    };
+
     let gdb = match gdb {
-        None => GDB_FALLBACK,
-        Some(ref s) if s.is_empty() => GDB_FALLBACK, // may be empty if configure found no gdb
-        Some(ref s) => s,
+        None => fallback_gdb(),
+        Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
+        Some(ref s) => s.to_owned(),
     };
 
     let mut version_line = None;
-    if let Ok(output) = Command::new(gdb).arg("--version").output() {
+    if let Ok(output) = Command::new(&gdb).arg("--version").output() {
         if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
             version_line = Some(first_line.to_string());
         }
@@ -809,7 +835,7 @@ fn analyze_gdb(gdb: Option<String>) -> (Option<String>, Option<u32>, bool) {
 
     let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
 
-    (Some(gdb.to_owned()), version, gdb_native_rust)
+    (Some(gdb), version, gdb_native_rust)
 }
 
 fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 306d9f8d852..63a282c227c 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -38,6 +38,7 @@ use std::process::{Child, Command, ExitStatus, Output, Stdio};
 use std::str;
 
 use extract_gdb_version;
+use is_android_gdb_target;
 
 #[cfg(windows)]
 fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
@@ -224,6 +225,19 @@ pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
 pub fn compute_stamp_hash(config: &Config) -> String {
     let mut hash = DefaultHasher::new();
     config.stage_id.hash(&mut hash);
+    match config.mode {
+        DebugInfoGdb => match config.gdb {
+            None => env::var_os("PATH").hash(&mut hash),
+            Some(ref s) if s.is_empty() => env::var_os("PATH").hash(&mut hash),
+            Some(ref s) => s.hash(&mut hash),
+        },
+        DebugInfoLldb => {
+            env::var_os("PATH").hash(&mut hash);
+            env::var_os("PYTHONPATH").hash(&mut hash);
+        },
+
+        _ => {},
+    };
     format!("{:x}", hash.finish())
 }
 
@@ -666,222 +680,217 @@ impl<'test> TestCx<'test> {
         let exe_file = self.make_exe_name();
 
         let debugger_run_result;
-        match &*self.config.target {
-            "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
-                cmds = cmds.replace("run", "continue");
+        if is_android_gdb_target(&self.config.target) {
+            cmds = cmds.replace("run", "continue");
 
-                let tool_path = match self.config.android_cross_path.to_str() {
-                    Some(x) => x.to_owned(),
-                    None => self.fatal("cannot find android cross path"),
-                };
+            let tool_path = match self.config.android_cross_path.to_str() {
+                Some(x) => x.to_owned(),
+                None => self.fatal("cannot find android cross path"),
+            };
 
-                // write debugger script
-                let mut script_str = String::with_capacity(2048);
-                script_str.push_str(&format!("set charset {}\n", Self::charset()));
-                script_str.push_str(&format!("set sysroot {}\n", tool_path));
-                script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
-                script_str.push_str("target remote :5039\n");
-                script_str.push_str(&format!(
-                    "set solib-search-path \
-                     ./{}/stage2/lib/rustlib/{}/lib/\n",
-                    self.config.host, self.config.target
-                ));
-                for line in &breakpoint_lines {
-                    script_str.push_str(
-                        &format!(
-                            "break {:?}:{}\n",
-                            self.testpaths.file.file_name().unwrap().to_string_lossy(),
-                            *line
-                        )[..],
-                    );
-                }
-                script_str.push_str(&cmds);
-                script_str.push_str("\nquit\n");
-
-                debug!("script_str = {}", script_str);
-                self.dump_output_file(&script_str, "debugger.script");
-
-                let adb_path = &self.config.adb_path;
-
-                Command::new(adb_path)
-                    .arg("push")
-                    .arg(&exe_file)
-                    .arg(&self.config.adb_test_dir)
-                    .status()
-                    .expect(&format!("failed to exec `{:?}`", adb_path));
-
-                Command::new(adb_path)
-                    .args(&["forward", "tcp:5039", "tcp:5039"])
-                    .status()
-                    .expect(&format!("failed to exec `{:?}`", adb_path));
-
-                let adb_arg = format!(
-                    "export LD_LIBRARY_PATH={}; \
-                     gdbserver{} :5039 {}/{}",
-                    self.config.adb_test_dir.clone(),
-                    if self.config.target.contains("aarch64") {
-                        "64"
-                    } else {
-                        ""
-                    },
-                    self.config.adb_test_dir.clone(),
-                    exe_file.file_name().unwrap().to_str().unwrap()
+            // write debugger script
+            let mut script_str = String::with_capacity(2048);
+            script_str.push_str(&format!("set charset {}\n", Self::charset()));
+            script_str.push_str(&format!("set sysroot {}\n", tool_path));
+            script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
+            script_str.push_str("target remote :5039\n");
+            script_str.push_str(&format!(
+                "set solib-search-path \
+                 ./{}/stage2/lib/rustlib/{}/lib/\n",
+                self.config.host, self.config.target
+            ));
+            for line in &breakpoint_lines {
+                script_str.push_str(
+                    &format!(
+                        "break {:?}:{}\n",
+                        self.testpaths.file.file_name().unwrap().to_string_lossy(),
+                        *line
+                    )[..],
                 );
+            }
+            script_str.push_str(&cmds);
+            script_str.push_str("\nquit\n");
 
-                debug!("adb arg: {}", adb_arg);
-                let mut adb = Command::new(adb_path)
-                    .args(&["shell", &adb_arg])
-                    .stdout(Stdio::piped())
-                    .stderr(Stdio::inherit())
-                    .spawn()
-                    .expect(&format!("failed to exec `{:?}`", adb_path));
-
-                // Wait for the gdbserver to print out "Listening on port ..."
-                // at which point we know that it's started and then we can
-                // execute the debugger below.
-                let mut stdout = BufReader::new(adb.stdout.take().unwrap());
-                let mut line = String::new();
-                loop {
-                    line.truncate(0);
-                    stdout.read_line(&mut line).unwrap();
-                    if line.starts_with("Listening on port 5039") {
-                        break;
-                    }
-                }
-                drop(stdout);
-
-                let debugger_script = self.make_out_name("debugger.script");
-                // FIXME (#9639): This needs to handle non-utf8 paths
-                let debugger_opts = vec![
-                    "-quiet".to_owned(),
-                    "-batch".to_owned(),
-                    "-nx".to_owned(),
-                    format!("-command={}", debugger_script.to_str().unwrap()),
-                ];
-
-                let mut gdb_path = tool_path;
-                gdb_path.push_str("/bin/gdb");
-                let Output {
-                    status,
-                    stdout,
-                    stderr,
-                } = Command::new(&gdb_path)
-                    .args(&debugger_opts)
-                    .output()
-                    .expect(&format!("failed to exec `{:?}`", gdb_path));
-                let cmdline = {
-                    let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
-                    gdb.args(&debugger_opts);
-                    let cmdline = self.make_cmdline(&gdb, "");
-                    logv(self.config, format!("executing {}", cmdline));
-                    cmdline
-                };
+            debug!("script_str = {}", script_str);
+            self.dump_output_file(&script_str, "debugger.script");
 
-                debugger_run_result = ProcRes {
-                    status,
-                    stdout: String::from_utf8(stdout).unwrap(),
-                    stderr: String::from_utf8(stderr).unwrap(),
-                    cmdline,
-                };
-                if adb.kill().is_err() {
-                    println!("Adb process is already finished.");
+            let adb_path = &self.config.adb_path;
+
+            Command::new(adb_path)
+                .arg("push")
+                .arg(&exe_file)
+                .arg(&self.config.adb_test_dir)
+                .status()
+                .expect(&format!("failed to exec `{:?}`", adb_path));
+
+            Command::new(adb_path)
+                .args(&["forward", "tcp:5039", "tcp:5039"])
+                .status()
+                .expect(&format!("failed to exec `{:?}`", adb_path));
+
+            let adb_arg = format!(
+                "export LD_LIBRARY_PATH={}; \
+                 gdbserver{} :5039 {}/{}",
+                self.config.adb_test_dir.clone(),
+                if self.config.target.contains("aarch64") {
+                    "64"
+                } else {
+                    ""
+                },
+                self.config.adb_test_dir.clone(),
+                exe_file.file_name().unwrap().to_str().unwrap()
+            );
+
+            debug!("adb arg: {}", adb_arg);
+            let mut adb = Command::new(adb_path)
+                .args(&["shell", &adb_arg])
+                .stdout(Stdio::piped())
+                .stderr(Stdio::inherit())
+                .spawn()
+                .expect(&format!("failed to exec `{:?}`", adb_path));
+
+            // Wait for the gdbserver to print out "Listening on port ..."
+            // at which point we know that it's started and then we can
+            // execute the debugger below.
+            let mut stdout = BufReader::new(adb.stdout.take().unwrap());
+            let mut line = String::new();
+            loop {
+                line.truncate(0);
+                stdout.read_line(&mut line).unwrap();
+                if line.starts_with("Listening on port 5039") {
+                    break;
                 }
             }
+            drop(stdout);
 
-            _ => {
-                let rust_src_root = self
-                    .config
-                    .find_rust_src_root()
-                    .expect("Could not find Rust source root");
-                let rust_pp_module_rel_path = Path::new("./src/etc");
-                let rust_pp_module_abs_path = rust_src_root
-                    .join(rust_pp_module_rel_path)
-                    .to_str()
-                    .unwrap()
-                    .to_owned();
-                // write debugger script
-                let mut script_str = String::with_capacity(2048);
-                script_str.push_str(&format!("set charset {}\n", Self::charset()));
-                script_str.push_str("show version\n");
-
-                match self.config.gdb_version {
-                    Some(version) => {
-                        println!(
-                            "NOTE: compiletest thinks it is using GDB version {}",
-                            version
-                        );
+            let debugger_script = self.make_out_name("debugger.script");
+            // FIXME (#9639): This needs to handle non-utf8 paths
+            let debugger_opts = vec![
+                "-quiet".to_owned(),
+                "-batch".to_owned(),
+                "-nx".to_owned(),
+                format!("-command={}", debugger_script.to_str().unwrap()),
+            ];
 
-                        if version > extract_gdb_version("7.4").unwrap() {
-                            // Add the directory containing the pretty printers to
-                            // GDB's script auto loading safe path
-                            script_str.push_str(&format!(
-                                "add-auto-load-safe-path {}\n",
-                                rust_pp_module_abs_path.replace(r"\", r"\\")
-                            ));
-                        }
-                    }
-                    _ => {
-                        println!(
-                            "NOTE: compiletest does not know which version of \
-                             GDB it is using"
-                        );
+            let gdb_path = self.config.gdb.as_ref().unwrap();
+            let Output {
+                status,
+                stdout,
+                stderr,
+            } = Command::new(&gdb_path)
+                .args(&debugger_opts)
+                .output()
+                .expect(&format!("failed to exec `{:?}`", gdb_path));
+            let cmdline = {
+                let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
+                gdb.args(&debugger_opts);
+                let cmdline = self.make_cmdline(&gdb, "");
+                logv(self.config, format!("executing {}", cmdline));
+                cmdline
+            };
+
+            debugger_run_result = ProcRes {
+                status,
+                stdout: String::from_utf8(stdout).unwrap(),
+                stderr: String::from_utf8(stderr).unwrap(),
+                cmdline,
+            };
+            if adb.kill().is_err() {
+                println!("Adb process is already finished.");
+            }
+        } else {
+            let rust_src_root = self
+                .config
+                .find_rust_src_root()
+                .expect("Could not find Rust source root");
+            let rust_pp_module_rel_path = Path::new("./src/etc");
+            let rust_pp_module_abs_path = rust_src_root
+                .join(rust_pp_module_rel_path)
+                .to_str()
+                .unwrap()
+                .to_owned();
+            // write debugger script
+            let mut script_str = String::with_capacity(2048);
+            script_str.push_str(&format!("set charset {}\n", Self::charset()));
+            script_str.push_str("show version\n");
+
+            match self.config.gdb_version {
+                Some(version) => {
+                    println!(
+                        "NOTE: compiletest thinks it is using GDB version {}",
+                        version
+                    );
+
+                    if version > extract_gdb_version("7.4").unwrap() {
+                        // Add the directory containing the pretty printers to
+                        // GDB's script auto loading safe path
+                        script_str.push_str(&format!(
+                            "add-auto-load-safe-path {}\n",
+                            rust_pp_module_abs_path.replace(r"\", r"\\")
+                        ));
                     }
                 }
+                _ => {
+                    println!(
+                        "NOTE: compiletest does not know which version of \
+                         GDB it is using"
+                    );
+                }
+            }
 
-                // The following line actually doesn't have to do anything with
-                // pretty printing, it just tells GDB to print values on one line:
-                script_str.push_str("set print pretty off\n");
+            // The following line actually doesn't have to do anything with
+            // pretty printing, it just tells GDB to print values on one line:
+            script_str.push_str("set print pretty off\n");
 
-                // Add the pretty printer directory to GDB's source-file search path
-                script_str.push_str(&format!("directory {}\n", rust_pp_module_abs_path));
+            // Add the pretty printer directory to GDB's source-file search path
+            script_str.push_str(&format!("directory {}\n", rust_pp_module_abs_path));
 
-                // Load the target executable
-                script_str.push_str(&format!(
-                    "file {}\n",
-                    exe_file.to_str().unwrap().replace(r"\", r"\\")
-                ));
+            // Load the target executable
+            script_str.push_str(&format!(
+                "file {}\n",
+                exe_file.to_str().unwrap().replace(r"\", r"\\")
+            ));
 
-                // Force GDB to print values in the Rust format.
-                if self.config.gdb_native_rust {
-                    script_str.push_str("set language rust\n");
-                }
+            // Force GDB to print values in the Rust format.
+            if self.config.gdb_native_rust {
+                script_str.push_str("set language rust\n");
+            }
 
-                // Add line breakpoints
-                for line in &breakpoint_lines {
-                    script_str.push_str(&format!(
-                        "break '{}':{}\n",
-                        self.testpaths.file.file_name().unwrap().to_string_lossy(),
-                        *line
-                    ));
-                }
+            // Add line breakpoints
+            for line in &breakpoint_lines {
+                script_str.push_str(&format!(
+                    "break '{}':{}\n",
+                    self.testpaths.file.file_name().unwrap().to_string_lossy(),
+                    *line
+                ));
+            }
 
-                script_str.push_str(&cmds);
-                script_str.push_str("\nquit\n");
+            script_str.push_str(&cmds);
+            script_str.push_str("\nquit\n");
 
-                debug!("script_str = {}", script_str);
-                self.dump_output_file(&script_str, "debugger.script");
+            debug!("script_str = {}", script_str);
+            self.dump_output_file(&script_str, "debugger.script");
 
-                let debugger_script = self.make_out_name("debugger.script");
+            let debugger_script = self.make_out_name("debugger.script");
 
-                // FIXME (#9639): This needs to handle non-utf8 paths
-                let debugger_opts = vec![
-                    "-quiet".to_owned(),
-                    "-batch".to_owned(),
-                    "-nx".to_owned(),
-                    format!("-command={}", debugger_script.to_str().unwrap()),
-                ];
+            // FIXME (#9639): This needs to handle non-utf8 paths
+            let debugger_opts = vec![
+                "-quiet".to_owned(),
+                "-batch".to_owned(),
+                "-nx".to_owned(),
+                format!("-command={}", debugger_script.to_str().unwrap()),
+            ];
 
-                let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
-                gdb.args(&debugger_opts)
-                    .env("PYTHONPATH", rust_pp_module_abs_path);
+            let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
+            gdb.args(&debugger_opts)
+                .env("PYTHONPATH", rust_pp_module_abs_path);
 
-                debugger_run_result = self.compose_and_run(
-                    gdb,
-                    self.config.run_lib_path.to_str().unwrap(),
-                    None,
-                    None,
-                );
-            }
+            debugger_run_result = self.compose_and_run(
+                gdb,
+                self.config.run_lib_path.to_str().unwrap(),
+                None,
+                None,
+            );
         }
 
         if !debugger_run_result.status.success() {