From 290f5b22752f98071bbc25530fc10a7169e671a3 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Wed, 4 Sep 2019 12:14:34 -0700 Subject: [PATCH 1/4] Use backtrace formatting from the backtrace crate --- Cargo.lock | 4 +- src/libstd/Cargo.toml | 2 +- src/libstd/panicking.rs | 8 +- src/libstd/sys_common/backtrace.rs | 235 +++++++++++------------------ 4 files changed, 96 insertions(+), 153 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e0d30647809a9..db83aacc69e45 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/libstd/Cargo.toml b/src/libstd/Cargo.toml index 157faa0af9bca..02a54e9fa72c6 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 952fd9ebfdf07..db4089c294812 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -158,7 +158,7 @@ pub fn take_hook() -> Box) + '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 bf37ff7ddbd3a..f434b62aced67 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::(); +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 { +pub fn log_enabled() -> Option { 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 { 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 - ` 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"")?, +/// 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("")) } - 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("")) - } - #[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("") - } - 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("") + } + }; + 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) } From 58ba1f51efab249411a932880e3c10d36355b166 Mon Sep 17 00:00:00 2001 From: Tim Vermeulen Date: Fri, 28 Jun 2019 14:43:32 +0200 Subject: [PATCH 2/4] Add Iterator comparison methods that take a comparison function --- src/libcore/iter/traits/iterator.rs | 109 ++++++++++++++++++++++++++-- src/libcore/tests/iter.rs | 56 ++++++++++++++ src/libcore/tests/lib.rs | 1 + 3 files changed, 160 insertions(+), 6 deletions(-) diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs index 8fd5fe0653eed..c09df3f7f22cb 100644 --- a/src/libcore/iter/traits/iterator.rs +++ b/src/libcore/iter/traits/iterator.rs @@ -2557,10 +2557,40 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().cmp([1].iter()), Ordering::Greater); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] - fn cmp(mut self, other: I) -> Ordering where + fn cmp(self, other: I) -> Ordering + where I: IntoIterator, 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(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(); @@ -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,10 +2631,49 @@ 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(mut self, other: I) -> Option where + fn partial_cmp(self, other: I) -> Option + where I: IntoIterator, Self::Item: PartialOrd, 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(mut self, other: I, mut partial_cmp: F) -> Option + where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, I::Item) -> Option, { let mut other = other.into_iter(); @@ -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,10 +2709,36 @@ pub trait Iterator { /// assert_eq!([1].iter().eq([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] - fn eq(mut self, other: I) -> bool where + fn eq(self, other: I) -> bool + where I: IntoIterator, Self::Item: PartialEq, 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(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(); @@ -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 8e0658d87c1fb..d7088cf891f77 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -57,6 +57,62 @@ fn test_multi_iter() { assert!(xs.iter().lt(xs.iter().skip(2))); } +#[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); diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index a3b108b2e9cea..3b4e47438e10a 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -32,6 +32,7 @@ #![feature(const_fn)] #![feature(iter_partition_in_place)] #![feature(iter_is_partitioned)] +#![feature(iter_order_by)] extern crate test; From 7dc3839b50d1ddb623aef0fbe76e982af460a18a Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 7 Sep 2019 19:34:25 +0300 Subject: [PATCH 3/4] resolve: Mark more erroneous imports as used --- src/librustc_resolve/resolve_imports.rs | 8 ++++++ .../ui/imports/unresolved-imports-used.rs | 14 +++++++--- .../ui/imports/unresolved-imports-used.stderr | 26 ++++++++++++++----- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index ca189e71800b3..132690dcd7df2 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/test/ui/imports/unresolved-imports-used.rs b/src/test/ui/imports/unresolved-imports-used.rs index d1461e7b041c5..5398dd63c8929 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 e8c827a6179bb..c9342d17a49d7 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`. From fb387088e2e327e4060290edc92a90d49669b04c Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 7 Sep 2019 12:21:32 -0700 Subject: [PATCH 4/4] rustdoc: fix diagnostic with mixed code block styles --- src/librustdoc/html/markdown.rs | 5 ++++- src/test/rustdoc-ui/invalid-syntax.rs | 8 ++++++++ src/test/rustdoc-ui/invalid-syntax.stderr | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 74413a7f905d4..8bf01d2cc1aac 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 { is_fenced = true; previous_offset + fence_idx } - None => offset, + None => { + is_fenced = false; + offset + } }; } } diff --git a/src/test/rustdoc-ui/invalid-syntax.rs b/src/test/rustdoc-ui/invalid-syntax.rs index 3ef66e273d0d0..97a0f4aaec1eb 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 36209e2927771..6f50edae65034 100644 --- a/src/test/rustdoc-ui/invalid-syntax.stderr +++ b/src/test/rustdoc-ui/invalid-syntax.stderr @@ -201,6 +201,24 @@ help: mark blocks that do not contain Rust code as text LL | /// ```text | ^^^^^^^ +error: unknown start of token: \ + --> :1:1 + | +1 | \____/ + | ^ + +warning: could not parse code block as Rust code + --> $DIR/invalid-syntax.rs:82:9 + | +LL | /// \____/ + | ^^^^^^ + +error: unknown start of token: \ + --> :1:1 + | +1 | \____/ + | ^ + error: unknown start of token: \ --> :1:1 |