about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-09-08 13:02:54 +0000
committerbors <bors@rust-lang.org>2019-09-08 13:02:54 +0000
commit2c0931e168671d7536b58563dc3664c948a8dcd3 (patch)
treefbbcf77cf17fbaf2dcbab14dcce6ccd1cf7658da
parent50362372d68eae9b02a351f0fab42962cd0518de (diff)
parent832b47aee6a5922bebd052e3261a6f839fba6aee (diff)
downloadrust-2c0931e168671d7536b58563dc3664c948a8dcd3.tar.gz
rust-2c0931e168671d7536b58563dc3664c948a8dcd3.zip
Auto merge of #64281 - Centril:rollup-inyqjf8, r=Centril
Rollup of 4 pull requests

Successful merges:

 - #62205 (Add Iterator comparison methods that take a comparison function)
 - #64152 (Use backtrace formatting from the backtrace crate)
 - #64265 (resolve: Mark more erroneous imports as used)
 - #64267 (rustdoc: fix diagnostic with mixed code block styles)

Failed merges:

r? @ghost
-rw-r--r--Cargo.lock4
-rw-r--r--src/libcore/iter/traits/iterator.rs109
-rw-r--r--src/libcore/tests/iter.rs56
-rw-r--r--src/libcore/tests/lib.rs1
-rw-r--r--src/librustc_resolve/resolve_imports.rs8
-rw-r--r--src/librustdoc/html/markdown.rs5
-rw-r--r--src/libstd/Cargo.toml2
-rw-r--r--src/libstd/panicking.rs8
-rw-r--r--src/libstd/sys_common/backtrace.rs235
-rw-r--r--src/test/rustdoc-ui/invalid-syntax.rs8
-rw-r--r--src/test/rustdoc-ui/invalid-syntax.stderr18
-rw-r--r--src/test/ui/imports/unresolved-imports-used.rs14
-rw-r--r--src/test/ui/imports/unresolved-imports-used.stderr26
13 files changed, 323 insertions, 171 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 108c2fbd8f5..328d96023bc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -109,9 +109,9 @@ dependencies = [
 
 [[package]]
 name = "backtrace"
-version = "0.3.35"
+version = "0.3.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1371048253fa3bac6704bfd6bbfc922ee9bdcee8881330d40f308b81cc5adc55"
+checksum = "5180c5a20655b14a819b652fd2378fa5f1697b6c9ddad3e695c2f9cedf6df4e2"
 dependencies = [
  "backtrace-sys",
  "cfg-if",
diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs
index 8fd5fe0653e..c09df3f7f22 100644
--- a/src/libcore/iter/traits/iterator.rs
+++ b/src/libcore/iter/traits/iterator.rs
@@ -2557,11 +2557,41 @@ pub trait Iterator {
     /// assert_eq!([1, 2].iter().cmp([1].iter()), Ordering::Greater);
     /// ```
     #[stable(feature = "iter_order", since = "1.5.0")]
-    fn cmp<I>(mut self, other: I) -> Ordering where
+    fn cmp<I>(self, other: I) -> Ordering
+    where
         I: IntoIterator<Item = Self::Item>,
         Self::Item: Ord,
         Self: Sized,
     {
+        self.cmp_by(other, |x, y| x.cmp(&y))
+    }
+
+    /// Lexicographically compares the elements of this `Iterator` with those
+    /// of another with respect to the specified comparison function.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(iter_order_by)]
+    ///
+    /// use std::cmp::Ordering;
+    ///
+    /// let xs = [1, 2, 3, 4];
+    /// let ys = [1, 4, 9, 16];
+    ///
+    /// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| x.cmp(&y)), Ordering::Less);
+    /// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| (x * x).cmp(&y)), Ordering::Equal);
+    /// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| (2 * x).cmp(&y)), Ordering::Greater);
+    /// ```
+    #[unstable(feature = "iter_order_by", issue = "0")]
+    fn cmp_by<I, F>(mut self, other: I, mut cmp: F) -> Ordering
+    where
+        Self: Sized,
+        I: IntoIterator,
+        F: FnMut(Self::Item, I::Item) -> Ordering,
+    {
         let mut other = other.into_iter();
 
         loop {
@@ -2579,7 +2609,7 @@ pub trait Iterator {
                 Some(val) => val,
             };
 
-            match x.cmp(&y) {
+            match cmp(x, y) {
                 Ordering::Equal => (),
                 non_eq => return non_eq,
             }
@@ -2601,11 +2631,50 @@ pub trait Iterator {
     /// assert_eq!([std::f64::NAN].iter().partial_cmp([1.].iter()), None);
     /// ```
     #[stable(feature = "iter_order", since = "1.5.0")]
-    fn partial_cmp<I>(mut self, other: I) -> Option<Ordering> where
+    fn partial_cmp<I>(self, other: I) -> Option<Ordering>
+    where
         I: IntoIterator,
         Self::Item: PartialOrd<I::Item>,
         Self: Sized,
     {
+        self.partial_cmp_by(other, |x, y| x.partial_cmp(&y))
+    }
+
+    /// Lexicographically compares the elements of this `Iterator` with those
+    /// of another with respect to the specified comparison function.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(iter_order_by)]
+    ///
+    /// use std::cmp::Ordering;
+    ///
+    /// let xs = [1.0, 2.0, 3.0, 4.0];
+    /// let ys = [1.0, 4.0, 9.0, 16.0];
+    ///
+    /// assert_eq!(
+    ///     xs.iter().partial_cmp_by(&ys, |&x, &y| x.partial_cmp(&y)),
+    ///     Some(Ordering::Less)
+    /// );
+    /// assert_eq!(
+    ///     xs.iter().partial_cmp_by(&ys, |&x, &y| (x * x).partial_cmp(&y)),
+    ///     Some(Ordering::Equal)
+    /// );
+    /// assert_eq!(
+    ///     xs.iter().partial_cmp_by(&ys, |&x, &y| (2.0 * x).partial_cmp(&y)),
+    ///     Some(Ordering::Greater)
+    /// );
+    /// ```
+    #[unstable(feature = "iter_order_by", issue = "0")]
+    fn partial_cmp_by<I, F>(mut self, other: I, mut partial_cmp: F) -> Option<Ordering>
+    where
+        Self: Sized,
+        I: IntoIterator,
+        F: FnMut(Self::Item, I::Item) -> Option<Ordering>,
+    {
         let mut other = other.into_iter();
 
         loop {
@@ -2623,7 +2692,7 @@ pub trait Iterator {
                 Some(val) => val,
             };
 
-            match x.partial_cmp(&y) {
+            match partial_cmp(x, y) {
                 Some(Ordering::Equal) => (),
                 non_eq => return non_eq,
             }
@@ -2640,11 +2709,37 @@ pub trait Iterator {
     /// assert_eq!([1].iter().eq([1, 2].iter()), false);
     /// ```
     #[stable(feature = "iter_order", since = "1.5.0")]
-    fn eq<I>(mut self, other: I) -> bool where
+    fn eq<I>(self, other: I) -> bool
+    where
         I: IntoIterator,
         Self::Item: PartialEq<I::Item>,
         Self: Sized,
     {
+        self.eq_by(other, |x, y| x == y)
+    }
+
+    /// Determines if the elements of this `Iterator` are equal to those of
+    /// another with respect to the specified equality function.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(iter_order_by)]
+    ///
+    /// let xs = [1, 2, 3, 4];
+    /// let ys = [1, 4, 9, 16];
+    ///
+    /// assert!(xs.iter().eq_by(&ys, |&x, &y| x * x == y));
+    /// ```
+    #[unstable(feature = "iter_order_by", issue = "0")]
+    fn eq_by<I, F>(mut self, other: I, mut eq: F) -> bool
+    where
+        Self: Sized,
+        I: IntoIterator,
+        F: FnMut(Self::Item, I::Item) -> bool,
+    {
         let mut other = other.into_iter();
 
         loop {
@@ -2658,7 +2753,9 @@ pub trait Iterator {
                 Some(val) => val,
             };
 
-            if x != y { return false }
+            if !eq(x, y) {
+                return false;
+            }
         }
     }
 
diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs
index 8e0658d87c1..d7088cf891f 100644
--- a/src/libcore/tests/iter.rs
+++ b/src/libcore/tests/iter.rs
@@ -58,6 +58,62 @@ fn test_multi_iter() {
 }
 
 #[test]
+fn test_cmp_by() {
+    use core::cmp::Ordering;
+
+    let f = |x: i32, y: i32| (x * x).cmp(&y);
+    let xs = || [1, 2, 3, 4].iter().copied();
+    let ys = || [1, 4, 16].iter().copied();
+
+    assert_eq!(xs().cmp_by(ys(), f), Ordering::Less);
+    assert_eq!(ys().cmp_by(xs(), f), Ordering::Greater);
+    assert_eq!(xs().cmp_by(xs().map(|x| x * x), f), Ordering::Equal);
+    assert_eq!(xs().rev().cmp_by(ys().rev(), f), Ordering::Greater);
+    assert_eq!(xs().cmp_by(ys().rev(), f), Ordering::Less);
+    assert_eq!(xs().cmp_by(ys().take(2), f), Ordering::Greater);
+}
+
+#[test]
+fn test_partial_cmp_by() {
+    use core::cmp::Ordering;
+    use core::f64;
+
+    let f = |x: i32, y: i32| (x * x).partial_cmp(&y);
+    let xs = || [1, 2, 3, 4].iter().copied();
+    let ys = || [1, 4, 16].iter().copied();
+
+    assert_eq!(xs().partial_cmp_by(ys(), f), Some(Ordering::Less));
+    assert_eq!(ys().partial_cmp_by(xs(), f), Some(Ordering::Greater));
+    assert_eq!(xs().partial_cmp_by(xs().map(|x| x * x), f), Some(Ordering::Equal));
+    assert_eq!(xs().rev().partial_cmp_by(ys().rev(), f), Some(Ordering::Greater));
+    assert_eq!(xs().partial_cmp_by(xs().rev(), f), Some(Ordering::Less));
+    assert_eq!(xs().partial_cmp_by(ys().take(2), f), Some(Ordering::Greater));
+
+    let f = |x: f64, y: f64| (x * x).partial_cmp(&y);
+    let xs = || [1.0, 2.0, 3.0, 4.0].iter().copied();
+    let ys = || [1.0, 4.0, f64::NAN, 16.0].iter().copied();
+
+    assert_eq!(xs().partial_cmp_by(ys(), f), None);
+    assert_eq!(ys().partial_cmp_by(xs(), f), Some(Ordering::Greater));
+}
+
+#[test]
+fn test_eq_by() {
+    let f = |x: i32, y: i32| x * x == y;
+    let xs = || [1, 2, 3, 4].iter().copied();
+    let ys = || [1, 4, 9, 16].iter().copied();
+
+    assert!(xs().eq_by(ys(), f));
+    assert!(!ys().eq_by(xs(), f));
+    assert!(!xs().eq_by(xs(), f));
+    assert!(!ys().eq_by(ys(), f));
+
+    assert!(!xs().take(3).eq_by(ys(), f));
+    assert!(!xs().eq_by(ys().take(3), f));
+    assert!(xs().take(3).eq_by(ys().take(3), f));
+}
+
+#[test]
 fn test_counter_from_iter() {
     let it = (0..).step_by(5).take(10);
     let xs: Vec<isize> = FromIterator::from_iter(it);
diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs
index b2c29aa2692..050195cd2ef 100644
--- a/src/libcore/tests/lib.rs
+++ b/src/libcore/tests/lib.rs
@@ -33,6 +33,7 @@
 #![feature(const_fn)]
 #![feature(iter_partition_in_place)]
 #![feature(iter_is_partitioned)]
+#![feature(iter_order_by)]
 
 extern crate test;
 
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index ca189e71800..132690dcd7d 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -673,6 +673,10 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
             self.throw_unresolved_import_error(errors.clone(), None);
         }
 
+        for import in &self.r.indeterminate_imports {
+            // Consider erroneous imports used to avoid duplicate diagnostics.
+            self.r.used_imports.insert((import.id, TypeNS));
+        }
         // Report unresolved imports only if no hard error was already reported
         // to avoid generating multiple errors on the same import.
         if !has_errors {
@@ -839,6 +843,10 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                                          true, directive.span, directive.crate_lint());
         let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
         directive.vis.set(orig_vis);
+        if let PathResult::Failed { .. } | PathResult::NonModule(..) = path_res {
+            // Consider erroneous imports used to avoid duplicate diagnostics.
+            self.r.used_imports.insert((directive.id, TypeNS));
+        }
         let module = match path_res {
             PathResult::Module(module) => {
                 // Consistency checks, analogous to `finalize_macro_resolutions`.
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 74413a7f905..8bf01d2cc1a 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -931,7 +931,10 @@ crate fn rust_code_blocks(md: &str) -> Vec<RustCodeBlock> {
                             is_fenced = true;
                             previous_offset + fence_idx
                         }
-                        None => offset,
+                        None => {
+                            is_fenced = false;
+                            offset
+                        }
                     };
                 }
             }
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml
index 18a46cf0b52..b5cbec7b0fa 100644
--- a/src/libstd/Cargo.toml
+++ b/src/libstd/Cargo.toml
@@ -26,7 +26,7 @@ unwind = { path = "../libunwind" }
 hashbrown = { version = "0.5.0", features = ['rustc-dep-of-std'] }
 
 [dependencies.backtrace]
-version = "0.3.35"
+version = "0.3.37"
 default-features = false # don't use coresymbolication on OSX
 features = [
   "rustc-dep-of-std", # enable build support for integrating into libstd
diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs
index 952fd9ebfdf..db4089c2948 100644
--- a/src/libstd/panicking.rs
+++ b/src/libstd/panicking.rs
@@ -158,7 +158,7 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
 
 fn default_hook(info: &PanicInfo<'_>) {
     #[cfg(feature = "backtrace")]
-    use crate::sys_common::backtrace;
+    use crate::sys_common::{backtrace as backtrace_mod};
 
     // If this is a double panic, make sure that we print a backtrace
     // for this panic. Otherwise only print it if logging is enabled.
@@ -167,9 +167,9 @@ fn default_hook(info: &PanicInfo<'_>) {
         let panics = update_panic_count(0);
 
         if panics >= 2 {
-            Some(backtrace::PrintFormat::Full)
+            Some(backtrace::PrintFmt::Full)
         } else {
-            backtrace::log_enabled()
+            backtrace_mod::log_enabled()
         }
     };
 
@@ -197,7 +197,7 @@ fn default_hook(info: &PanicInfo<'_>) {
             static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
 
             if let Some(format) = log_backtrace {
-                let _ = backtrace::print(err, format);
+                let _ = backtrace_mod::print(err, format);
             } else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) {
                 let _ = writeln!(err, "note: run with `RUST_BACKTRACE=1` \
                                        environment variable to display a backtrace.");
diff --git a/src/libstd/sys_common/backtrace.rs b/src/libstd/sys_common/backtrace.rs
index bf37ff7ddbd..f434b62aced 100644
--- a/src/libstd/sys_common/backtrace.rs
+++ b/src/libstd/sys_common/backtrace.rs
@@ -2,23 +2,20 @@
 /// supported platforms.
 
 use crate::env;
+use crate::fmt;
 use crate::io;
 use crate::io::prelude::*;
-use crate::mem;
 use crate::path::{self, Path};
-use crate::ptr;
 use crate::sync::atomic::{self, Ordering};
 use crate::sys::mutex::Mutex;
 
-use backtrace::{BytesOrWideString, Frame, Symbol};
-
-pub const HEX_WIDTH: usize = 2 + 2 * mem::size_of::<usize>();
+use backtrace::{BacktraceFmt, BytesOrWideString, PrintFmt};
 
 /// Max number of frames to print.
 const MAX_NB_FRAMES: usize = 100;
 
 /// Prints the current backtrace.
-pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
+pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
     static LOCK: Mutex = Mutex::new();
 
     // There are issues currently linking libbacktrace into tests, and in
@@ -39,26 +36,66 @@ pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
     }
 }
 
-fn _print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
-    writeln!(w, "stack backtrace:")?;
+fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
+    struct DisplayBacktrace {
+        format: PrintFmt,
+    }
+    impl fmt::Display for DisplayBacktrace {
+        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+            _print_fmt(fmt, self.format)
+        }
+    }
+    write!(w, "{}", DisplayBacktrace { format })
+}
 
-    let mut printer = Printer::new(format, w);
+fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
+    let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
+        output_filename(fmt, bows, print_fmt)
+    };
+    let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
+    bt_fmt.add_context()?;
+    let mut skipped = false;
     unsafe {
+        let mut idx = 0;
+        let mut res = Ok(());
         backtrace::trace_unsynchronized(|frame| {
+            if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
+                skipped = true;
+                return false;
+            }
+
             let mut hit = false;
+            let mut stop = false;
             backtrace::resolve_frame_unsynchronized(frame, |symbol| {
                 hit = true;
-                printer.output(frame, Some(symbol));
+                if print_fmt == PrintFmt::Short {
+                    if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
+                        if sym.contains("__rust_begin_short_backtrace") {
+                            skipped = true;
+                            stop = true;
+                            return;
+                        }
+                    }
+                }
+
+                res = bt_fmt.frame().symbol(frame, symbol);
             });
+            if stop {
+                return false;
+            }
             if !hit {
-                printer.output(frame, None);
+                res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
             }
-            !printer.done
+
+            idx += 1;
+            res.is_ok()
         });
+        res?;
     }
-    if printer.skipped {
+    bt_fmt.finish()?;
+    if skipped {
         writeln!(
-            w,
+            fmt,
             "note: Some details are omitted, \
              run with `RUST_BACKTRACE=full` for a verbose backtrace."
         )?;
@@ -77,33 +114,24 @@ where
     f()
 }
 
-/// Controls how the backtrace should be formatted.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub enum PrintFormat {
-    /// Show only relevant data from the backtrace.
-    Short = 2,
-    /// Show all the frames with absolute path for files.
-    Full = 3,
-}
-
 // For now logging is turned off by default, and this function checks to see
 // whether the magical environment variable is present to see if it's turned on.
-pub fn log_enabled() -> Option<PrintFormat> {
+pub fn log_enabled() -> Option<PrintFmt> {
     static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
     match ENABLED.load(Ordering::SeqCst) {
         0 => {}
         1 => return None,
-        2 => return Some(PrintFormat::Short),
-        _ => return Some(PrintFormat::Full),
+        2 => return Some(PrintFmt::Short),
+        _ => return Some(PrintFmt::Full),
     }
 
     let val = env::var_os("RUST_BACKTRACE").and_then(|x| {
         if &x == "0" {
             None
         } else if &x == "full" {
-            Some(PrintFormat::Full)
+            Some(PrintFmt::Full)
         } else {
-            Some(PrintFormat::Short)
+            Some(PrintFmt::Short)
         }
     });
     ENABLED.store(
@@ -116,130 +144,45 @@ pub fn log_enabled() -> Option<PrintFormat> {
     val
 }
 
-struct Printer<'a, 'b> {
-    format: PrintFormat,
-    done: bool,
-    skipped: bool,
-    idx: usize,
-    out: &'a mut (dyn Write + 'b),
-}
-
-impl<'a, 'b> Printer<'a, 'b> {
-    fn new(format: PrintFormat, out: &'a mut (dyn Write + 'b)) -> Printer<'a, 'b> {
-        Printer { format, done: false, skipped: false, idx: 0, out }
-    }
-
-    /// Prints the symbol of the backtrace frame.
-    ///
-    /// These output functions should now be used everywhere to ensure consistency.
-    /// You may want to also use `output_fileline`.
-    fn output(&mut self, frame: &Frame, symbol: Option<&Symbol>) {
-        if self.idx > MAX_NB_FRAMES {
-            self.done = true;
-            self.skipped = true;
-            return;
-        }
-        if self._output(frame, symbol).is_err() {
-            self.done = true;
-        }
-        self.idx += 1;
-    }
-
-    fn _output(&mut self, frame: &Frame, symbol: Option<&Symbol>) -> io::Result<()> {
-        if self.format == PrintFormat::Short {
-            if let Some(sym) = symbol.and_then(|s| s.name()).and_then(|s| s.as_str()) {
-                if sym.contains("__rust_begin_short_backtrace") {
-                    self.skipped = true;
-                    self.done = true;
-                    return Ok(());
-                }
-            }
-
-            // Remove the `17: 0x0 - <unknown>` line.
-            if self.format == PrintFormat::Short && frame.ip() == ptr::null_mut() {
-                self.skipped = true;
-                return Ok(());
-            }
-        }
-
-        match self.format {
-            PrintFormat::Full => {
-                write!(self.out, "  {:2}: {:2$?} - ", self.idx, frame.ip(), HEX_WIDTH)?
-            }
-            PrintFormat::Short => write!(self.out, "  {:2}: ", self.idx)?,
-        }
-
-        match symbol.and_then(|s| s.name()) {
-            Some(symbol) => {
-                match self.format {
-                    PrintFormat::Full => write!(self.out, "{}", symbol)?,
-                    // Strip the trailing hash if short mode.
-                    PrintFormat::Short => write!(self.out, "{:#}", symbol)?,
-                }
-            }
-            None => self.out.write_all(b"<unknown>")?,
+/// Prints the filename of the backtrace frame.
+///
+/// See also `output`.
+fn output_filename(
+    fmt: &mut fmt::Formatter<'_>,
+    bows: BytesOrWideString<'_>,
+    print_fmt: PrintFmt,
+) -> fmt::Result {
+    #[cfg(windows)]
+    let path_buf;
+    let file = match bows {
+        #[cfg(unix)]
+        BytesOrWideString::Bytes(bytes) => {
+            use crate::os::unix::prelude::*;
+            Path::new(crate::ffi::OsStr::from_bytes(bytes))
         }
-        self.out.write_all(b"\n")?;
-        if let Some(sym) = symbol {
-            self.output_fileline(sym)?;
+        #[cfg(not(unix))]
+        BytesOrWideString::Bytes(bytes) => {
+            Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>"))
         }
-        Ok(())
-    }
-
-    /// Prints the filename and line number of the backtrace frame.
-    ///
-    /// See also `output`.
-    fn output_fileline(&mut self, symbol: &Symbol) -> io::Result<()> {
         #[cfg(windows)]
-        let path_buf;
-        let file = match symbol.filename_raw() {
-            #[cfg(unix)]
-            Some(BytesOrWideString::Bytes(bytes)) => {
-                use crate::os::unix::prelude::*;
-                Path::new(crate::ffi::OsStr::from_bytes(bytes))
-            }
-            #[cfg(not(unix))]
-            Some(BytesOrWideString::Bytes(bytes)) => {
-                Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>"))
-            }
-            #[cfg(windows)]
-            Some(BytesOrWideString::Wide(wide)) => {
-                use crate::os::windows::prelude::*;
-                path_buf = crate::ffi::OsString::from_wide(wide);
-                Path::new(&path_buf)
-            }
-            #[cfg(not(windows))]
-            Some(BytesOrWideString::Wide(_wide)) => {
-                Path::new("<unknown>")
-            }
-            None => return Ok(()),
-        };
-        let line = match symbol.lineno() {
-            Some(line) => line,
-            None => return Ok(()),
-        };
-        // prior line: "  ##: {:2$} - func"
-        self.out.write_all(b"")?;
-        match self.format {
-            PrintFormat::Full => write!(self.out, "           {:1$}", "", HEX_WIDTH)?,
-            PrintFormat::Short => write!(self.out, "           ")?,
+        BytesOrWideString::Wide(wide) => {
+            use crate::os::windows::prelude::*;
+            path_buf = crate::ffi::OsString::from_wide(wide);
+            Path::new(&path_buf)
         }
-
-        let mut already_printed = false;
-        if self.format == PrintFormat::Short && file.is_absolute() {
-            if let Ok(cwd) = env::current_dir() {
-                if let Ok(stripped) = file.strip_prefix(&cwd) {
-                    if let Some(s) = stripped.to_str() {
-                        write!(self.out, "  at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?;
-                        already_printed = true;
-                    }
+        #[cfg(not(windows))]
+        BytesOrWideString::Wide(_wide) => {
+            Path::new("<unknown>")
+        }
+    };
+    if print_fmt == PrintFmt::Short && file.is_absolute() {
+        if let Ok(cwd) = env::current_dir() {
+            if let Ok(stripped) = file.strip_prefix(&cwd) {
+                if let Some(s) = stripped.to_str() {
+                    return write!(fmt, ".{}{}", path::MAIN_SEPARATOR, s);
                 }
             }
         }
-        if !already_printed {
-            write!(self.out, "  at {}:{}", file.display(), line)?;
-        }
-
-        self.out.write_all(b"\n")
     }
+    fmt::Display::fmt(&file.display(), fmt)
 }
diff --git a/src/test/rustdoc-ui/invalid-syntax.rs b/src/test/rustdoc-ui/invalid-syntax.rs
index 3ef66e273d0..97a0f4aaec1 100644
--- a/src/test/rustdoc-ui/invalid-syntax.rs
+++ b/src/test/rustdoc-ui/invalid-syntax.rs
@@ -74,3 +74,11 @@ pub fn empty_rust() {}
 ///
 /// ```
 pub fn empty_rust_with_whitespace() {}
+
+/// ```
+/// let x = 1;
+/// ```
+///
+///     \____/
+///
+pub fn indent_after_fenced() {}
diff --git a/src/test/rustdoc-ui/invalid-syntax.stderr b/src/test/rustdoc-ui/invalid-syntax.stderr
index 36209e29277..6f50edae650 100644
--- a/src/test/rustdoc-ui/invalid-syntax.stderr
+++ b/src/test/rustdoc-ui/invalid-syntax.stderr
@@ -202,6 +202,24 @@ LL | /// ```text
    |     ^^^^^^^
 
 error: unknown start of token: \
+ --> <doctest>:1:1
+  |
+1 | \____/
+  | ^
+
+warning: could not parse code block as Rust code
+  --> $DIR/invalid-syntax.rs:82:9
+   |
+LL | ///     \____/
+   |         ^^^^^^
+
+error: unknown start of token: \
+ --> <rustdoc-highlighting>:1:1
+  |
+1 | \____/
+  | ^
+
+error: unknown start of token: \
  --> <rustdoc-highlighting>:1:1
   |
 1 | \_
diff --git a/src/test/ui/imports/unresolved-imports-used.rs b/src/test/ui/imports/unresolved-imports-used.rs
index d1461e7b041..5398dd63c89 100644
--- a/src/test/ui/imports/unresolved-imports-used.rs
+++ b/src/test/ui/imports/unresolved-imports-used.rs
@@ -1,12 +1,18 @@
-// There should be *no* unused import errors.
+// There should be *one* unused import error.
 #![deny(unused_imports)]
 
 mod qux {
    fn quz() {}
+   pub fn quy() {}
 }
 
-use qux::quz; //~ ERROR function `quz` is private
-use qux::bar; //~ ERROR unresolved import `qux::bar`
-use foo::bar; //~ ERROR unresolved import `foo`
+use qux::quz;  //~ ERROR function `quz` is private
+use qux::bar;  //~ ERROR unresolved import `qux::bar`
+use foo::bar;
+use baz::*;
+use qux::bar2; //~ ERROR unresolved import `qux::bar2`
+use foo2::bar2;
+use baz2::*;
+use qux::quy;  //~ ERROR unused import
 
 fn main() {}
diff --git a/src/test/ui/imports/unresolved-imports-used.stderr b/src/test/ui/imports/unresolved-imports-used.stderr
index e8c827a6179..c9342d17a49 100644
--- a/src/test/ui/imports/unresolved-imports-used.stderr
+++ b/src/test/ui/imports/unresolved-imports-used.stderr
@@ -1,22 +1,34 @@
 error[E0432]: unresolved import `qux::bar`
-  --> $DIR/unresolved-imports-used.rs:9:5
+  --> $DIR/unresolved-imports-used.rs:10:5
    |
 LL | use qux::bar;
    |     ^^^^^^^^ no `bar` in `qux`
 
-error[E0432]: unresolved import `foo`
-  --> $DIR/unresolved-imports-used.rs:10:5
+error[E0432]: unresolved import `qux::bar2`
+  --> $DIR/unresolved-imports-used.rs:13:5
    |
-LL | use foo::bar;
-   |     ^^^ maybe a missing crate `foo`?
+LL | use qux::bar2;
+   |     ^^^^^^^^^ no `bar2` in `qux`
 
 error[E0603]: function `quz` is private
-  --> $DIR/unresolved-imports-used.rs:8:10
+  --> $DIR/unresolved-imports-used.rs:9:10
    |
 LL | use qux::quz;
    |          ^^^
 
-error: aborting due to 3 previous errors
+error: unused import: `qux::quy`
+  --> $DIR/unresolved-imports-used.rs:16:5
+   |
+LL | use qux::quy;
+   |     ^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/unresolved-imports-used.rs:2:9
+   |
+LL | #![deny(unused_imports)]
+   |         ^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0432, E0603.
 For more information about an error, try `rustc --explain E0432`.