diff --git a/src/librustc_trans/back/msvc/mod.rs b/src/librustc_trans/back/msvc/mod.rs
index 87d3fd1c0e827..6f0baa86579e0 100644
--- a/src/librustc_trans/back/msvc/mod.rs
+++ b/src/librustc_trans/back/msvc/mod.rs
@@ -28,7 +28,7 @@
 //! shell or MSYS shells.
 //!
 //! As a high-level note, all logic in this module for looking up various
-//! paths/files is copied over from Clang in its MSVCToolChain.cpp file, but
+//! paths/files is based on Microsoft's logic in their vcvars bat files, but
 //! comments can also be found below leading through the various code paths.
 
 use std::process::Command;
@@ -42,290 +42,225 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
     use std::env;
     use std::ffi::OsString;
     use std::fs;
-    use std::io;
     use std::path::{Path, PathBuf};
-    use self::registry::{RegistryKey, LOCAL_MACHINE};
+    use self::registry::{LOCAL_MACHINE};
 
-    // When finding the link.exe binary the 32-bit version is at the top level
-    // but the versions to cross to other architectures are stored in
-    // sub-folders. Unknown architectures also just bail out early to return the
-    // standard `link.exe` command.
-    let extra = match &sess.target.target.arch[..] {
-        "x86" => "",
-        "x86_64" => "amd64",
-        "arm" => "arm",
+    let arch = &sess.target.target.arch;
+    let (binsub, libsub, vclibsub) =
+        match (bin_subdir(arch), lib_subdir(arch), vc_lib_subdir(arch)) {
+        (Some(x), Some(y), Some(z)) => (x, y, z),
         _ => return Command::new("link.exe"),
     };
 
-    let vs_install_dir = get_vs_install_dir();
-
-    // First up, we need to find the `link.exe` binary itself, and there's a few
-    // locations that we can look. First up is the standard VCINSTALLDIR
-    // environment variable which is normally set by the vcvarsall.bat file. If
-    // an environment is set up manually by whomever's driving the compiler then
-    // we shouldn't muck with that decision and should instead respect that.
+    // First we need to figure out whether the environment is already correctly
+    // configured by vcvars. We do this by looking at the environment variable
+    // `VCINSTALLDIR` which is always set by vcvars, and unlikely to be set
+    // otherwise. If it is defined, then we derive the path to `link.exe` from
+    // that and trust that everything else is configured correctly.
+    //
+    // If `VCINSTALLDIR` wasn't defined (or we couldn't find the linker where it
+    // claimed it should be), then we resort to finding everything ourselves.
+    // First we find where the latest version of MSVC is installed and what
+    // version it is. Then based on the version we find the appropriate SDKs.
+    //
+    // For MSVC 14 (VS 2015) we look for the Win10 SDK and failing that we look
+    // for the Win8.1 SDK. We also look for the Universal CRT.
     //
-    // Next up is looking in PATH itself. Here we look for `cl.exe` and then
-    // assume that `link.exe` is next to it if we find it. Note that we look for
-    // `cl.exe` because MinGW ships /usr/bin/link.exe which is normally found in
-    // PATH but we're not interested in finding that.
+    // For MSVC 12 (VS 2013) we look for the Win8.1 SDK.
     //
-    // Finally we read the Windows registry to discover the VS install root.
-    // From here we probe for `link.exe` just to make sure that it exists.
-    let mut cmd = env::var_os("VCINSTALLDIR").and_then(|dir| {
+    // For MSVC 11 (VS 2012) we look for the Win8 SDK.
+    //
+    // For all other versions the user has to execute the appropriate vcvars bat
+    // file themselves to configure the environment.
+    //
+    // If despite our best efforts we are still unable to find MSVC then we just
+    // blindly call `link.exe` and hope for the best.
+    return env::var_os("VCINSTALLDIR").and_then(|dir| {
+        debug!("Environment already configured by user. Assuming it works.");
         let mut p = PathBuf::from(dir);
         p.push("bin");
-        p.push(extra);
+        p.push(binsub);
         p.push("link.exe");
-        if fs::metadata(&p).is_ok() {Some(p)} else {None}
-    }).or_else(|| {
-        env::var_os("PATH").and_then(|path| {
-            env::split_paths(&path).find(|path| {
-                fs::metadata(&path.join("cl.exe")).is_ok()
-            }).map(|p| {
-                p.join("link.exe")
-            })
-        })
+        if !p.is_file() { return None }
+        Some(Command::new(p))
     }).or_else(|| {
-        vs_install_dir.as_ref().and_then(|p| {
-            let mut p = p.join("VC/bin");
-            p.push(extra);
-            p.push("link.exe");
-            if fs::metadata(&p).is_ok() {Some(p)} else {None}
+        get_vc_dir().and_then(|(ver, vcdir)| {
+            debug!("Found VC installation directory {:?}", vcdir);
+            let mut linker = vcdir.clone();
+            linker.push("bin");
+            linker.push(binsub);
+            linker.push("link.exe");
+            if !linker.is_file() { return None }
+            let mut cmd = Command::new(linker);
+            add_lib(&mut cmd, &vcdir.join("lib").join(vclibsub));
+            if ver == "14.0" {
+                if let Some(dir) = get_ucrt_dir() {
+                    debug!("Found Universal CRT {:?}", dir);
+                    add_lib(&mut cmd, &dir.join("ucrt").join(libsub));
+                }
+                if let Some(dir) = get_sdk10_dir() {
+                    debug!("Found Win10 SDK {:?}", dir);
+                    add_lib(&mut cmd, &dir.join("um").join(libsub));
+                } else if let Some(dir) = get_sdk81_dir() {
+                    debug!("Found Win8.1 SDK {:?}", dir);
+                    add_lib(&mut cmd, &dir.join("um").join(libsub));
+                }
+            } else if ver == "12.0" {
+                if let Some(dir) = get_sdk81_dir() {
+                    debug!("Found Win8.1 SDK {:?}", dir);
+                    add_lib(&mut cmd, &dir.join("um").join(libsub));
+                }
+            } else { // ver == "11.0"
+                if let Some(dir) = get_sdk8_dir() {
+                    debug!("Found Win8 SDK {:?}", dir);
+                    add_lib(&mut cmd, &dir.join("um").join(libsub));
+                }
+            }
+            Some(cmd)
         })
-    }).map(|linker| {
-        Command::new(linker)
     }).unwrap_or_else(|| {
+        debug!("Failed to locate linker.");
         Command::new("link.exe")
     });
 
-    // The MSVC linker uses the LIB environment variable as the default lookup
-    // path for libraries. This environment variable is normally set up by the
-    // VS shells, so we only want to start adding our own pieces if it's not
-    // set.
-    //
-    // If we're adding our own pieces, then we need to add a few primary
-    // directories to the default search path for the linker. The first is in
-    // the VS install direcotry, the next is the Windows SDK directory, and the
-    // last is the possible UCRT installation directory.
-    //
-    // The UCRT is a recent addition to Visual Studio installs (2015 at the time
-    // of this writing), and it's in the normal windows SDK folder, but there
-    // apparently aren't registry keys pointing to it. As a result we detect the
-    // installation and then add it manually. This logic will probably need to
-    // be tweaked over time...
-    if env::var_os("LIB").is_none() {
-        if let Some(mut vs_install_dir) = vs_install_dir {
-            vs_install_dir.push("VC/lib");
-            vs_install_dir.push(extra);
-            let mut arg = OsString::from("/LIBPATH:");
-            arg.push(&vs_install_dir);
-            cmd.arg(arg);
+    // A convenience function to make the above code simpler
+    fn add_lib(cmd: &mut Command, lib: &Path) {
+        let mut arg: OsString = "/LIBPATH:".into();
+        arg.push(lib);
+        cmd.arg(arg);
+    }
 
-            if let Some((ucrt_root, vers)) = ucrt_install_dir(&vs_install_dir) {
-                if let Some(arch) = windows_sdk_v8_subdir(sess) {
-                    let mut arg = OsString::from("/LIBPATH:");
-                    arg.push(ucrt_root.join("Lib").join(vers)
-                                      .join("ucrt").join(arch));
-                    cmd.arg(arg);
-                }
-            }
-        }
-        if let Some(path) = get_windows_sdk_lib_path(sess) {
-            let mut arg = OsString::from("/LIBPATH:");
-            arg.push(&path);
-            cmd.arg(arg);
-        }
+    // To find MSVC we look in a specific registry key for the newest of the
+    // three versions that we support.
+    fn get_vc_dir() -> Option<(&'static str, PathBuf)> {
+        LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VisualStudio\SxS\VC7".as_ref())
+        .ok().and_then(|key| {
+            ["14.0", "12.0", "11.0"].iter().filter_map(|ver| {
+                key.query_str(ver).ok().map(|p| (*ver, p.into()))
+            }).next()
+        })
     }
 
-    return cmd;
+    // To find the Universal CRT we look in a specific registry key for where
+    // all the Universal CRTs are located and then sort them asciibetically to
+    // find the newest version. While this sort of sorting isn't ideal,  it is
+    // what vcvars does so that's good enough for us.
+    fn get_ucrt_dir() -> Option<PathBuf> {
+        LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Windows Kits\Installed Roots".as_ref())
+        .ok().and_then(|key| {
+            key.query_str("KitsRoot10").ok()
+        }).and_then(|root| {
+            fs::read_dir(Path::new(&root).join("Lib")).ok()
+        }).and_then(|readdir| {
+            let mut dirs: Vec<_> = readdir.filter_map(|dir| {
+                dir.ok()
+            }).map(|dir| {
+                dir.path()
+            }).filter(|dir| {
+                dir.components().last().and_then(|c| {
+                    c.as_os_str().to_str()
+                }).map(|c| c.starts_with("10.")).unwrap_or(false)
+            }).collect();
+            dirs.sort();
+            dirs.pop()
+        })
+    }
 
-    // When looking for the Visual Studio installation directory we look in a
-    // number of locations in varying degrees of precedence:
-    //
-    // 1. The Visual Studio registry keys
-    // 2. The Visual Studio Express registry keys
-    // 3. A number of somewhat standard environment variables
-    //
-    // If we find a hit from any of these keys then we strip off the IDE/Tools
-    // folders which are typically found at the end.
-    //
-    // As a final note, when we take a look at the registry keys they're
-    // typically found underneath the version of what's installed, but we don't
-    // quite know what's installed. As a result we probe all sub-keys of the two
-    // keys we're looking at to find out the maximum version of what's installed
-    // and we use that root directory.
-    fn get_vs_install_dir() -> Option<PathBuf> {
-        LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VisualStudio".as_ref()).or_else(|_| {
-            LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VCExpress".as_ref())
-        }).ok().and_then(|key| {
-            max_version(&key).and_then(|(_vers, key)| {
-                key.query_str("InstallDir").ok()
-            })
-        }).or_else(|| {
-            env::var_os("VS120COMNTOOLS")
-        }).or_else(|| {
-            env::var_os("VS100COMNTOOLS")
-        }).or_else(|| {
-            env::var_os("VS90COMNTOOLS")
-        }).or_else(|| {
-            env::var_os("VS80COMNTOOLS")
-        }).map(PathBuf::from).and_then(|mut dir| {
-            if dir.ends_with("Common7/IDE") || dir.ends_with("Common7/Tools") {
-                dir.pop();
-                dir.pop();
-                Some(dir)
-            } else {
-                None
-            }
+    // Vcvars finds the correct version of the Windows 10 SDK by looking
+    // for the include um/Windows.h because sometimes a given version will
+    // only have UCRT bits without the rest of the SDK. Since we only care about
+    // libraries and not includes, we just look for the folder `um` in the lib
+    // section. Like we do for the Universal CRT, we sort the possibilities
+    // asciibetically to find the newest one as that is what vcvars does.
+    fn get_sdk10_dir() -> Option<PathBuf> {
+        LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0".as_ref())
+        .ok().and_then(|key| {
+            key.query_str("InstallationFolder").ok()
+        }).and_then(|root| {
+            fs::read_dir(Path::new(&root).join("lib")).ok()
+        }).and_then(|readdir| {
+            let mut dirs: Vec<_> = readdir.filter_map(|dir| dir.ok())
+                .map(|dir| dir.path()).collect();
+            dirs.sort();
+            dirs.into_iter().rev().filter(|dir| {
+                dir.join("um").is_dir()
+            }).next()
         })
     }
 
-    // Given a registry key, look at all the sub keys and find the one which has
-    // the maximal numeric value.
-    //
-    // Returns the name of the maximal key as well as the opened maximal key.
-    fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> {
-        let mut max_vers = 0;
-        let mut max_key = None;
-        for subkey in key.iter().filter_map(|k| k.ok()) {
-            let val = subkey.to_str().and_then(|s| {
-                s.trim_left_matches("v").replace(".", "").parse().ok()
-            });
-            let val = match val {
-                Some(s) => s,
-                None => continue,
-            };
-            if val > max_vers {
-                if let Ok(k) = key.open(&subkey) {
-                    max_vers = val;
-                    max_key = Some((subkey, k));
-                }
-            }
-        }
-        return max_key
+    // Interestingly there are several subdirectories, `win7` `win8` and
+    // `winv6.3`. Vcvars seems to only care about `winv6.3` though, so the same
+    // applies to us. Note that if we were targetting kernel mode drivers
+    // instead of user mode applications, we would care.
+    fn get_sdk81_dir() -> Option<PathBuf> {
+        LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1".as_ref())
+        .ok().and_then(|key| {
+            key.query_str("InstallationFolder").ok()
+        }).map(|root| {
+            Path::new(&root).join("lib").join("winv6.3")
+        })
     }
 
-    fn get_windows_sdk_path() -> Option<(PathBuf, usize, Option<OsString>)> {
-        let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows";
-        let key = LOCAL_MACHINE.open(key.as_ref());
-        let (n, k) = match key.ok().as_ref().and_then(max_version) {
-            Some(p) => p,
-            None => return None,
-        };
-        let mut parts = n.to_str().unwrap().trim_left_matches("v").splitn(2, ".");
-        let major = parts.next().unwrap().parse::<usize>().unwrap();
-        let _minor = parts.next().unwrap().parse::<usize>().unwrap();
-        k.query_str("InstallationFolder").ok().map(|folder| {
-            let ver = k.query_str("ProductVersion");
-            (PathBuf::from(folder), major, ver.ok())
+    fn get_sdk8_dir() -> Option<PathBuf> {
+        LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0".as_ref())
+        .ok().and_then(|key| {
+            key.query_str("InstallationFolder").ok()
+        }).map(|root| {
+            Path::new(&root).join("lib").join("win8")
         })
     }
 
-    fn get_windows_sdk_lib_path(sess: &Session) -> Option<PathBuf> {
-        let (mut path, major, ver) = match get_windows_sdk_path() {
-            Some(p) => p,
-            None => return None,
-        };
-        path.push("Lib");
-        if major <= 7 {
-            // In Windows SDK 7.x, x86 libraries are directly in the Lib folder,
-            // x64 libraries are inside, and it's not necessary to link against
-            // the SDK 7.x when targeting ARM or other architectures.
-            let x86 = match &sess.target.target.arch[..] {
-                "x86" => true,
-                "x86_64" => false,
-                _ => return None,
-            };
-            Some(if x86 {path} else {path.join("x64")})
-        } else if major <= 8 {
-            // Windows SDK 8.x installs libraries in a folder whose names
-            // depend on the version of the OS you're targeting. By default
-            // choose the newest, which usually corresponds to the version of
-            // the OS you've installed the SDK on.
-            let extra = match windows_sdk_v8_subdir(sess) {
-                Some(e) => e,
-                None => return None,
-            };
-            ["winv6.3", "win8", "win7"].iter().map(|p| path.join(p)).find(|part| {
-                fs::metadata(part).is_ok()
-            }).map(|path| {
-                path.join("um").join(extra)
-            })
-        } else if let Some(mut ver) = ver {
-            // Windows SDK 10 splits the libraries into architectures the same
-            // as Windows SDK 8.x, except for the addition of arm64.
-            // Additionally, the SDK 10 is split by Windows 10 build numbers
-            // rather than the OS version like the SDK 8.x does.
-            let extra = match windows_sdk_v10_subdir(sess) {
-                Some(e) => e,
-                None => return None,
-            };
-            // To get the correct directory we need to get the Windows SDK 10
-            // version, and so far it looks like the "ProductVersion" of the SDK
-            // corresponds to the folder name that the libraries are located in
-            // except that the folder contains an extra ".0". For now just
-            // append a ".0" to look for find the directory we're in. This logic
-            // will likely want to be refactored one day.
-            ver.push(".0");
-            let p = path.join(ver).join("um").join(extra);
-            fs::metadata(&p).ok().map(|_| p)
+    // When choosing the linker toolchain to use, we have to choose the one
+    // which matches the host architecture. Otherwise we end up in situations
+    // where someone on 32-bit Windows is trying to cross compile to 64-bit and
+    // it tries to invoke the native 64-bit linker which won't work.
+    //
+    // FIXME - This currently functions based on the host architecture of rustc
+    // itself but it should instead detect the bitness of the OS itself.
+    //
+    // FIXME - Figure out what happens when the host architecture is arm.
+    //
+    // FIXME - Some versions of MSVC may not come with all these toolchains.
+    // Consider returning an array of toolchains and trying them one at a time
+    // until the linker is found.
+    fn bin_subdir(arch: &str) -> Option<&'static str> {
+        if cfg!(target_arch = "x86_64") {
+            match arch {
+                "x86" => Some("amd64_x86"),
+                "x86_64" => Some("amd64"),
+                "arm" => Some("amd64_arm"),
+                _ => None,
+            }
+        } else if cfg!(target_arch = "x86") {
+            match arch {
+                "x86" => Some(""),
+                "x86_64" => Some("x86_amd64"),
+                "arm" => Some("x86_arm"),
+                _ => None,
+            }
         } else { None }
     }
-
-    fn windows_sdk_v8_subdir(sess: &Session) -> Option<&'static str> {
-        match &sess.target.target.arch[..] {
+    fn lib_subdir(arch: &str) -> Option<&'static str> {
+        match arch {
             "x86" => Some("x86"),
             "x86_64" => Some("x64"),
             "arm" => Some("arm"),
-            _ => return None,
+            _ => None,
         }
     }
-
-    fn windows_sdk_v10_subdir(sess: &Session) -> Option<&'static str> {
-        match &sess.target.target.arch[..] {
-            "x86" => Some("x86"),
-            "x86_64" => Some("x64"),
+    // MSVC's x86 libraries are not in a subfolder
+    fn vc_lib_subdir(arch: &str) -> Option<&'static str> {
+        match arch {
+            "x86" => Some(""),
+            "x86_64" => Some("amd64"),
             "arm" => Some("arm"),
-            "aarch64" => Some("arm64"), // FIXME - Check if aarch64 is correct
-            _ => return None,
+            _ => None,
         }
     }
-
-    fn ucrt_install_dir(vs_install_dir: &Path) -> Option<(PathBuf, String)> {
-        let is_vs_14 = vs_install_dir.iter().filter_map(|p| p.to_str()).any(|s| {
-            s == "Microsoft Visual Studio 14.0"
-        });
-        if !is_vs_14 {
-            return None
-        }
-        let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
-        let sdk_dir = LOCAL_MACHINE.open(key.as_ref()).and_then(|p| {
-            p.query_str("KitsRoot10")
-        }).map(PathBuf::from);
-        let sdk_dir = match sdk_dir {
-            Ok(p) => p,
-            Err(..) => return None,
-        };
-        (move || -> io::Result<_> {
-            let mut max = None;
-            let mut max_s = None;
-            for entry in try!(fs::read_dir(&sdk_dir.join("Lib"))) {
-                let entry = try!(entry);
-                if let Ok(s) = entry.file_name().into_string() {
-                    if let Ok(u) = s.replace(".", "").parse::<usize>() {
-                        if Some(u) > max {
-                            max = Some(u);
-                            max_s = Some(s);
-                        }
-                    }
-                }
-            }
-            Ok(max_s.map(|m| (sdk_dir, m)))
-        })().ok().and_then(|x| x)
-    }
 }
 
+// If we're not on Windows, then there's no registry to search through and MSVC
+// wouldn't be able to run, so we just call `link.exe` and hope for the best.
 #[cfg(not(windows))]
 pub fn link_exe_cmd(_sess: &Session) -> Command {
     Command::new("link.exe")
diff --git a/src/librustc_trans/back/msvc/registry.rs b/src/librustc_trans/back/msvc/registry.rs
index 63fb19c9772a8..24179a0cccd22 100644
--- a/src/librustc_trans/back/msvc/registry.rs
+++ b/src/librustc_trans/back/msvc/registry.rs
@@ -11,7 +11,6 @@
 use std::io;
 use std::ffi::{OsString, OsStr};
 use std::os::windows::prelude::*;
-use std::ops::RangeFrom;
 use std::ptr;
 use libc::{c_void, c_long};
 
@@ -34,7 +33,6 @@ const KEY_NOTIFY: REGSAM = 0x0010;
 const SYNCHRONIZE: REGSAM = 0x00100000;
 const REG_SZ: DWORD = 1;
 const ERROR_SUCCESS: i32 = 0;
-const ERROR_NO_MORE_ITEMS: DWORD = 259;
 
 enum __HKEY__ {}
 pub type HKEY = *mut __HKEY__;
@@ -56,14 +54,6 @@ extern "system" {
                         lpType: LPDWORD,
                         lpData: LPBYTE,
                         lpcbData: LPDWORD) -> LONG;
-    fn RegEnumKeyExW(hKey: HKEY,
-                     dwIndex: DWORD,
-                     lpName: LPWSTR,
-                     lpcName: LPDWORD,
-                     lpReserved: LPDWORD,
-                     lpClass: LPWSTR,
-                     lpcClass: LPDWORD,
-                     lpftLastWriteTime: PFILETIME) -> LONG;
     fn RegCloseKey(hKey: HKEY) -> LONG;
 }
 
@@ -76,11 +66,6 @@ enum Repr {
     Owned(OwnedKey),
 }
 
-pub struct Iter<'a> {
-    idx: RangeFrom<DWORD>,
-    key: &'a RegistryKey,
-}
-
 unsafe impl Sync for RegistryKey {}
 unsafe impl Send for RegistryKey {}
 
@@ -108,10 +93,6 @@ impl RegistryKey {
         }
     }
 
-    pub fn iter(&self) -> Iter {
-        Iter { idx: 0.., key: self }
-    }
-
     pub fn query_str(&self, name: &str) -> io::Result<OsString> {
         let name: &OsStr = name.as_ref();
         let name = name.encode_wide().chain(Some(0)).collect::<Vec<_>>();
@@ -155,25 +136,3 @@ impl Drop for OwnedKey {
         unsafe { RegCloseKey(self.0); }
     }
 }
-
-impl<'a> Iterator for Iter<'a> {
-    type Item = io::Result<OsString>;
-
-    fn next(&mut self) -> Option<io::Result<OsString>> {
-        self.idx.next().and_then(|i| unsafe {
-            let mut v = Vec::with_capacity(256);
-            let mut len = v.capacity() as DWORD;
-            let ret = RegEnumKeyExW(self.key.raw(), i, v.as_mut_ptr(), &mut len,
-                                    ptr::null_mut(), ptr::null_mut(), ptr::null_mut(),
-                                    ptr::null_mut());
-            if ret == ERROR_NO_MORE_ITEMS as LONG {
-                None
-            } else if ret != ERROR_SUCCESS {
-                Some(Err(io::Error::from_raw_os_error(ret as i32)))
-            } else {
-                v.set_len(len as usize);
-                Some(Ok(OsString::from_wide(&v)))
-            }
-        })
-    }
-}
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 1b450092dbd5d..af6510cb3870e 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -261,7 +261,7 @@ pub fn main_args(args: &[String]) -> isize {
 
     match (should_test, markdown_input) {
         (true, true) => {
-            return markdown::test(input, libs, externs, test_args)
+            return markdown::test(input, cfgs, libs, externs, test_args)
         }
         (true, false) => {
             return test::run(input, cfgs, libs, externs, test_args, crate_name)
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index ac64fd3bec0e7..03d2c1a1b4d0d 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -142,13 +142,13 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches,
 }
 
 /// Run any tests/code examples in the markdown file `input`.
-pub fn test(input: &str, libs: SearchPaths, externs: core::Externs,
+pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: core::Externs,
             mut test_args: Vec<String>) -> isize {
     let input_str = load_or_return!(input, 1, 2);
 
     let mut opts = TestOptions::default();
     opts.no_crate_inject = true;
-    let mut collector = Collector::new(input.to_string(), libs, externs,
+    let mut collector = Collector::new(input.to_string(), cfgs, libs, externs,
                                        true, opts);
     find_testable_code(&input_str, &mut collector);
     test_args.insert(0, "rustdoctest".to_string());
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 3e303b29d5c7a..3322794c7781e 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -85,7 +85,7 @@ pub fn run(input: &str,
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
 
     let mut cfg = config::build_configuration(&sess);
-    cfg.extend(config::parse_cfgspecs(cfgs));
+    cfg.extend(config::parse_cfgspecs(cfgs.clone()));
     let krate = driver::phase_1_parse_input(&sess, cfg, &input);
     let krate = driver::phase_2_configure_and_expand(&sess, &cstore, krate,
                                                      "rustdoc-test", None)
@@ -122,6 +122,7 @@ pub fn run(input: &str,
     let (krate, _) = passes::unindent_comments(krate);
 
     let mut collector = Collector::new(krate.name.to_string(),
+                                       cfgs,
                                        libs,
                                        externs,
                                        false,
@@ -168,7 +169,7 @@ fn scrape_test_config(krate: &::rustc_front::hir::Crate) -> TestOptions {
     return opts;
 }
 
-fn runtest(test: &str, cratename: &str, libs: SearchPaths,
+fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
            externs: core::Externs,
            should_panic: bool, no_run: bool, as_test_harness: bool,
            opts: &TestOptions) {
@@ -239,7 +240,8 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths,
 
     let outdir = TempDir::new("rustdoctest").ok().expect("rustdoc needs a tempdir");
     let out = Some(outdir.path().to_path_buf());
-    let cfg = config::build_configuration(&sess);
+    let mut cfg = config::build_configuration(&sess);
+    cfg.extend(config::parse_cfgspecs(cfgs));
     let libdir = sess.target_filesearch(PathKind::All).get_lib_path();
     let mut control = driver::CompileController::basic();
     if no_run {
@@ -349,6 +351,7 @@ fn partition_source(s: &str) -> (String, String) {
 pub struct Collector {
     pub tests: Vec<testing::TestDescAndFn>,
     names: Vec<String>,
+    cfgs: Vec<String>,
     libs: SearchPaths,
     externs: core::Externs,
     cnt: usize,
@@ -359,11 +362,12 @@ pub struct Collector {
 }
 
 impl Collector {
-    pub fn new(cratename: String, libs: SearchPaths, externs: core::Externs,
+    pub fn new(cratename: String, cfgs: Vec<String>, libs: SearchPaths, externs: core::Externs,
                use_headers: bool, opts: TestOptions) -> Collector {
         Collector {
             tests: Vec::new(),
             names: Vec::new(),
+            cfgs: cfgs,
             libs: libs,
             externs: externs,
             cnt: 0,
@@ -384,6 +388,7 @@ impl Collector {
             format!("{}_{}", self.names.join("::"), self.cnt)
         };
         self.cnt += 1;
+        let cfgs = self.cfgs.clone();
         let libs = self.libs.clone();
         let externs = self.externs.clone();
         let cratename = self.cratename.to_string();
@@ -399,6 +404,7 @@ impl Collector {
             testfn: testing::DynTestFn(Box::new(move|| {
                 runtest(&test,
                         &cratename,
+                        cfgs,
                         libs,
                         externs,
                         should_panic,
diff --git a/src/test/rustdoc/issue-30252.rs b/src/test/rustdoc/issue-30252.rs
new file mode 100644
index 0000000000000..11d161fe188e9
--- /dev/null
+++ b/src/test/rustdoc/issue-30252.rs
@@ -0,0 +1,16 @@
+// Copyright 2015 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.
+
+// compile-flags:--test --cfg feature="bar"
+
+/// ```rust
+/// assert_eq!(cfg!(feature = "bar"), true);
+/// ```
+pub fn foo() {}